{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:28.537558Z",
     "start_time": "2025-02-06T14:04:28.531690Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:40.689441Z",
     "iopub.status.busy": "2025-02-07T15:37:40.689277Z",
     "iopub.status.idle": "2025-02-07T15:37:42.919218Z",
     "shell.execute_reply": "2025-02-07T15:37:42.918516Z",
     "shell.execute_reply.started": "2025-02-07T15:37:40.689419Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c29f1d6fe09a9955",
   "metadata": {},
   "source": [
    "# 数据加载\n",
    "\n",
    "- 采用WMT16的德语和英语平行语料库，数据集主页：[WMT16](https://www.statmt.org/wmt16/multimodal-task.html#task1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d047d1eed0336fbe",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:28.942222Z",
     "start_time": "2025-02-06T14:04:28.938639Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.920686Z",
     "iopub.status.busy": "2025-02-07T15:37:42.920244Z",
     "iopub.status.idle": "2025-02-07T15:37:42.923326Z",
     "shell.execute_reply": "2025-02-07T15:37:42.922728Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.920663Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# # 调用脚本文件\n",
    "# !pip install sacremoses\n",
    "# !sh data_multi30k.sh wmt16 wmt16_cut de en"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a930b5668e422750",
   "metadata": {},
   "source": [
    "## Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c0f0f4781f5e042f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.020273Z",
     "start_time": "2025-02-06T14:04:29.011246Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.924105Z",
     "iopub.status.busy": "2025-02-07T15:37:42.923924Z",
     "iopub.status.idle": "2025-02-07T15:37:42.932332Z",
     "shell.execute_reply": "2025-02-07T15:37:42.931738Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.924084Z"
    }
   },
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "    def __init__(self, mode=\"train\", max_length=128,\n",
    "                 overwrite_cache=False, data_dir=\"wmt16\"):\n",
    "        self.data_dir = Path(data_dir)\n",
    "        cache_path = self.data_dir / \".cache\" / f\"de2en_{mode}_{max_length}.npy\"\n",
    "\n",
    "        if overwrite_cache or not cache_path.exists():\n",
    "            # parents=True：如果父目录不存在，会自动创建所有必要的父目录。\n",
    "            # exist_ok=True：如果目录已经存在，不会抛出错误。\n",
    "            cache_path.parent.mkdir(parents=True, exist_ok=True)\n",
    "            # 读取源语言文件所有行\n",
    "            with open(self.data_dir / f\"{mode}_src.bpe\", \"r\", encoding=\"utf8\") as f:\n",
    "                self.src = f.readlines()\n",
    "            # 读取目标语言文件所有行\n",
    "            with open(self.data_dir / f\"{mode}_trg.bpe\", \"r\", encoding=\"utf8\") as f:\n",
    "                self.trg = f.readlines()\n",
    "\n",
    "            filtered_src = []\n",
    "            filtered_trg = []\n",
    "            # max length filter,超出最大长度的句子舍弃\n",
    "            for src, trg in zip(self.src, self.trg):\n",
    "                # 过滤长度超过最大长度的句子\n",
    "                if len(src) <= max_length and len(trg) <= max_length:\n",
    "                    filtered_src.append(src.strip())  # 去掉句子前后的空格\n",
    "                    filtered_trg.append(trg.strip())\n",
    "            filtered_src = np.array(filtered_src)\n",
    "            filtered_trg = np.array(filtered_trg)\n",
    "            #allow_pickle=True允许保存对象数组，将过滤后的数据保存为 NumPy 数组，存储在缓存文件中\n",
    "            np.save(\n",
    "                cache_path,\n",
    "                {\"src\": filtered_src, \"trg\": filtered_trg},\n",
    "                allow_pickle=True\n",
    "            )\n",
    "            print(f\"save cache to {cache_path}\")\n",
    "\n",
    "        else:\n",
    "            # allow_pickle=True允许保存对象数组\n",
    "            cache_dict = np.load(cache_path, allow_pickle=True).item()\n",
    "            print(f\"load {mode} dataset from {cache_path}\")\n",
    "            filtered_src = cache_dict[\"src\"]\n",
    "            filtered_trg = cache_dict[\"trg\"]\n",
    "\n",
    "        self.src = filtered_src\n",
    "        self.trg = filtered_trg\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "# train_ds = LangPairDataset(\"train\")\n",
    "# val_ds = LangPairDataset(\"val\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a2a0f23be5871b84",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.097548Z",
     "start_time": "2025-02-06T14:04:29.094294Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.933535Z",
     "iopub.status.busy": "2025-02-07T15:37:42.933145Z",
     "iopub.status.idle": "2025-02-07T15:37:42.936143Z",
     "shell.execute_reply": "2025-02-07T15:37:42.935468Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.933502Z"
    }
   },
   "outputs": [],
   "source": [
    "# print(len(train_ds))\n",
    "# print(len(val_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2eaf27846b953da4",
   "metadata": {},
   "source": [
    "## Tokenizer\n",
    "\n",
    "这里有两种处理方式，分别对应着 encoder 和 decoder 的 word embedding 是否共享，这里实现共享的方案"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "df6173e2f201c301",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.169362Z",
     "start_time": "2025-02-06T14:04:29.138854Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.938532Z",
     "iopub.status.busy": "2025-02-07T15:37:42.938121Z",
     "iopub.status.idle": "2025-02-07T15:37:42.968392Z",
     "shell.execute_reply": "2025-02-07T15:37:42.967732Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.938497Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 18107/18107 [00:00<00:00, 919223.71it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab_size: 18111\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "word2idx = {\n",
    "    \"[PAD]\": 0,  # 填充 token\n",
    "    \"[BOS]\": 1,  # begin of sentence\n",
    "    \"[UNK]\": 2,  # 未知 token\n",
    "    \"[EOS]\": 3,  # end of sentence\n",
    "}\n",
    "\n",
    "idx2word = {value: key for key, value in word2idx.items()}\n",
    "index = len(idx2word)\n",
    "threshold = 1  # 出现次数低于此的token舍弃\n",
    "\n",
    "with open(\"wmt16/vocab\", \"r\", encoding=\"utf8\") as fd:\n",
    "    for line in tqdm(fd.readlines()):\n",
    "        token, counts = line.strip().split()\n",
    "        if int(counts) >= threshold:\n",
    "            word2idx[token] = index\n",
    "            idx2word[index] = token\n",
    "            index += 1\n",
    "\n",
    "vocab_size = len(word2idx)\n",
    "print(\"vocab_size: {}\".format(vocab_size))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "461056213fba3ed4",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.226412Z",
     "start_time": "2025-02-06T14:04:29.217164Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.969686Z",
     "iopub.status.busy": "2025-02-07T15:37:42.969400Z",
     "iopub.status.idle": "2025-02-07T15:37:42.979628Z",
     "shell.execute_reply": "2025-02-07T15:37:42.979101Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.969651Z"
    }
   },
   "outputs": [],
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=128,\n",
    "                 pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True,\n",
    "               return_mask=False):\n",
    "        # 如果padding_first == True，则padding加载前面，否则加载后面\n",
    "        # return_mask: 是否返回mask(掩码），mask用于指示哪些是padding的，哪些是真实的token \n",
    "        # 若启动bos_eos，则在句首和句尾分别添加bos和eos，则 add_bos=True, add_eos=True即都为1\n",
    "        max_length = min(self.max_length, add_bos + add_eos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        for text in text_list:\n",
    "            # 如果词表中没有这个词，就用unk_idx代替，indices是一个list,里面是每个词的index,也就是一个样本的index\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_eos - add_bos]]\n",
    "            if add_bos:\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:  # padding加载前面，超参可以调\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:  # padding加载后面\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list)  # 转换为tensor\n",
    "        # mask是一个和input_ids一样大小的tensor，0代表token，1代表padding，mask用于去除padding的影响\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.float64)\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                # 如果词表中没有这个词，就用unk_idx代替\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":  # 如果到达eos，就结束\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":  # 如果到达pad，就结束\n",
    "                    break\n",
    "                text.append(word)  # 单词添加到列表中\n",
    "            # 把列表中的单词拼接，变为一个句子\n",
    "            text_list.append(\" \".join(text) if not split else text)\n",
    "        return text_list\n",
    "\n",
    "\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "\n",
    "# tokenizer.encode([[\"hello\"], [\"hello\", \"world\"]], add_bos=True, add_eos=False)\n",
    "# raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "# indices = tokenizer.encode(raw_text, padding_first=False, add_bos=True, add_eos=True)\n",
    "# decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "# print(\"raw text\")\n",
    "# for raw in raw_text:\n",
    "#     print(raw)\n",
    "# print(\"indices\")\n",
    "# for index in indices:\n",
    "#     print(index)\n",
    "# print(\"decode text\")\n",
    "# for decode in decode_text:\n",
    "#     print(decode)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "628dded826e86608",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.254457Z",
     "start_time": "2025-02-06T14:04:29.250423Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.980697Z",
     "iopub.status.busy": "2025-02-07T15:37:42.980268Z",
     "iopub.status.idle": "2025-02-07T15:37:42.982927Z",
     "shell.execute_reply": "2025-02-07T15:37:42.982445Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.980673Z"
    }
   },
   "outputs": [],
   "source": [
    "# for i,j in train_ds:\n",
    "#     print(len(i))\n",
    "#     print(len(j))\n",
    "#     break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d43df9ce1d4860bb",
   "metadata": {},
   "source": [
    "## Transformer Batch Sampler\n",
    "\n",
    "> Sentence pairs were batched together by approximate sequence length. Each training batch contained a set of sentence pairs containing approximately 25000 source tokens and 25000 target tokens\n",
    "句子按照序列长度差不多的分到一个批次。 每个训练批次包含一组句子对，其中包含大约 25000 个源标记和 25000 个目标标记"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7c072c0e714bd8ea",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.298858Z",
     "start_time": "2025-02-06T14:04:29.288864Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.983695Z",
     "iopub.status.busy": "2025-02-07T15:37:42.983532Z",
     "iopub.status.idle": "2025-02-07T15:37:42.990071Z",
     "shell.execute_reply": "2025-02-07T15:37:42.989586Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.983675Z"
    }
   },
   "outputs": [],
   "source": [
    "# 下面的info对象\n",
    "class SampleInfo:\n",
    "    def __init__(self, i, lens):\n",
    "        \"\"\"\n",
    "        记录文本对的序号和长度信息\n",
    "        输入：\n",
    "            - i (int): 文本对的序号。\n",
    "            - lens (list): 文本对源语言和目标语言的长度\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        # 加一是考虑填补在文本前后的特殊词元，lens[0]和lens[1]分别表示源语言和目标语言的长度\n",
    "        self.max_len = max(lens[0], lens[1]) + 1\n",
    "        self.src_len = lens[0] + 1  # 加一是考虑填补在文本前后的特殊词元\n",
    "        self.trg_len = lens[1] + 1  # 加一是考虑填补在文本前后的特殊词元\n",
    "\n",
    "\n",
    "# 一个批量生成器，根据词元数目的限制来控制批量的大小。\n",
    "# 它会根据传入的样本信息，在不超过设定大小的情况下，逐步构建批量。\n",
    "class TokenBatchCreator:\n",
    "    def __init__(self, batch_size):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        batch_size (int): 用于限制批量的大小。\n",
    "        功能:\n",
    "        初始化了一个空的批量列表 _batch。\n",
    "        设定了初始的最大长度为 -1。\n",
    "        存储了传入的 batch_size。\n",
    "        \"\"\"\n",
    "        self._batch = []  # 这个就是之前的batch_size，就是一个batch内有多少个样本\n",
    "        self.max_len = -1\n",
    "        self.batch_size = batch_size  # 限制批量的大小,假设是4096\n",
    "\n",
    "    def append(self, info: SampleInfo):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        info (SampleInfo): 文本对的信息。\n",
    "        功能:\n",
    "        接收一个 SampleInfo 对象，并根据其最大长度信息更新当前批量的最大长度。\n",
    "        如果将新的样本加入批量后超过了批量大小限制，它会返回已有的批量并将新的样本加入新的批量。\n",
    "        否则，它会更新最大长度并将样本添加到当前批量中。\n",
    "        \"\"\"\n",
    "        # 更新当前批量的最大长度\n",
    "        cur_len = info.max_len  # 当前样本的长度\n",
    "        max_len = max(self.max_len, cur_len)  # 每来一个样本，更新当前批次的最大长度\n",
    "\n",
    "        # 如果新的样本加入批量后超过大小限制，则将已有的批量返回，新的样本加入新的批量\n",
    "        # 同一批样本计算时，短的样本向最长的样本对齐，所以用max_len来计算\n",
    "        if max_len * (len(self._batch) + 1) > self.batch_size:\n",
    "            # result_batch保存原有的_batch，_batch清空\n",
    "            self._batch, result_batch = [], self._batch\n",
    "            self._batch.append(info)  #箱子里的第一条样本，放入\n",
    "            # 因为是当前batch的第一个样本，所以它的长度就是当前长度\n",
    "            self.max_len = cur_len\n",
    "            return result_batch\n",
    "        else:\n",
    "            self.max_len = max_len\n",
    "            self._batch.append(info)\n",
    "            return None\n",
    "\n",
    "    # 将类的方法转换为属性，从而实现对类属性的访问、修改和删除的控制\n",
    "    @property\n",
    "    def batch(self):\n",
    "        return self._batch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "c1dd026c49a90114",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.363561Z",
     "start_time": "2025-02-06T14:04:29.355876Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.990987Z",
     "iopub.status.busy": "2025-02-07T15:37:42.990648Z",
     "iopub.status.idle": "2025-02-07T15:37:42.999020Z",
     "shell.execute_reply": "2025-02-07T15:37:42.998547Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.990965Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.data import BatchSampler\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class TransformerBatchSampler(BatchSampler):\n",
    "    def __init__(self, dataset, batch_size, shuffle_batch=False,\n",
    "                 clip_last_batch=False, seed=0):\n",
    "        \"\"\"\n",
    "        批量采样器\n",
    "        输入:\n",
    "            - dataset: 数据集\n",
    "            - batch_size: 批量大小\n",
    "            - shuffle_batch: 是否对生成的批量进行洗牌\n",
    "            - clip_last_batch: 是否裁剪最后剩下的数据\n",
    "            - seed: 随机数种子\n",
    "        \"\"\"\n",
    "        self._dataset = dataset\n",
    "        self._batch_size = batch_size\n",
    "        self._shuffle_batch = shuffle_batch\n",
    "        self._clip_last_batch = clip_last_batch\n",
    "        self._seed = seed\n",
    "        self._random = np.random\n",
    "        self._random.seed(seed)\n",
    "\n",
    "        self._sample_infos = []\n",
    "        # 根据数据集中的每个样本，创建了对应的 SampleInfo 对象，包含了样本的索引和长度信息\n",
    "        for i, data in enumerate(self._dataset):\n",
    "            lens = [len(data[0]), len(data[1])]  #输入和输出的长度计算放到lens中\n",
    "            self._sample_infos.append(SampleInfo(i, lens))\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\"\n",
    "        对数据集中的样本进行排序，排序规则是先按源语言长度排序，如果相同则按目标语言长度排序。\n",
    "        使用 TokenBatchCreator 逐步组装批量数据，当满足批量大小时返回一个批量的样本信息。\n",
    "        如果不裁剪最后一个批次的数据且存在剩余样本，则将这些样本组成最后一个批次。\n",
    "        如果需要对批量进行洗牌，则对批次进行洗牌操作。\n",
    "        通过迭代器，抛出每个批量的样本在数据集中的索引。\n",
    "        \"\"\"\n",
    "        # 排序，如果源语言长度相同则按照目标语言的长度排列\n",
    "        infos = sorted(self._sample_infos,\n",
    "                       key=lambda x: (x.src_len, x.trg_len))\n",
    "        # 组装批量，所有的batch都放入batch_infos\n",
    "        batch_infos = []\n",
    "        # 批量生成器\n",
    "        batch_creator = TokenBatchCreator(self._batch_size)\n",
    "        for info in infos:\n",
    "            batch = batch_creator.append(info)\n",
    "            # 存够一个batch的样本信息后，会把这个batch返回，否则返回为None\n",
    "            if batch is not None:\n",
    "                batch_infos.append(batch)\n",
    "\n",
    "        # 是否抛弃最后批量的文本对\n",
    "        # 如果最后一个batch的样本数目不足一个batch，则将其剩余的样本作为最后一个batch\n",
    "        if not self._clip_last_batch and len(batch_creator.batch) != 0:\n",
    "            batch_infos.append(batch_creator.batch)  # 最后一个batch\n",
    "\n",
    "        # 打乱batch\n",
    "        # 这里的shuffle是对batch_infos进行shuffle，而不是对batch_infos中的样本进行shuffle\n",
    "        if self._shuffle_batch:\n",
    "            self._random.shuffle(batch_infos)\n",
    "\n",
    "        self.batch_number = len(batch_infos)\n",
    "\n",
    "        # 抛出一个批量的文本对在数据集中的序号\n",
    "        for batch in batch_infos:\n",
    "            # info.i 是文本对的索引\n",
    "            batch_indices = [info.i for info in batch]\n",
    "            yield batch_indices\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回批量的数量\n",
    "        \"\"\"\n",
    "        if hasattr(self, \"batch_number\"):\n",
    "            return self.batch_number\n",
    "        # 计算批量的数量,没有用到下面的情况，不用看\n",
    "        batch_number = (len(self._dataset) +\n",
    "                        self._batch_size) // self._batch_size\n",
    "        return batch_number\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "71ae51fb9433db98",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.444646Z",
     "start_time": "2025-02-06T14:04:29.441583Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:42.999750Z",
     "iopub.status.busy": "2025-02-07T15:37:42.999572Z",
     "iopub.status.idle": "2025-02-07T15:37:43.002307Z",
     "shell.execute_reply": "2025-02-07T15:37:43.001847Z",
     "shell.execute_reply.started": "2025-02-07T15:37:42.999730Z"
    }
   },
   "outputs": [],
   "source": [
    "# sampler = TransformerBatchSampler(train_ds, batch_size=4096, shuffle_batch=True)\n",
    "# \n",
    "# #为什么这里每个批量的样本对数目不一样呢？长度*batch_number>4096的时候，就会返回上一个batch，然后新的样本加入新的batch,具体要看TokenBatchCreator的40行\n",
    "# for idx, batch in enumerate(sampler):\n",
    "#     print(\"第{}批量的数据中含有文本对是：{}，数量为：{}\".format(idx, batch, len(batch)))\n",
    "#     if idx >= 3:\n",
    "#         break\n",
    "# \n",
    "# print(\"-\"*50)\n",
    "# print(len(sampler))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98b1add788035b2a",
   "metadata": {},
   "source": [
    "## DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "24e99727ef49605",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.533317Z",
     "start_time": "2025-02-06T14:04:29.526671Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.003239Z",
     "iopub.status.busy": "2025-02-07T15:37:43.002971Z",
     "iopub.status.idle": "2025-02-07T15:37:43.007812Z",
     "shell.execute_reply": "2025-02-07T15:37:43.007363Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.003219Z"
    }
   },
   "outputs": [],
   "source": [
    "def collate_fn(batch, tokenizer):\n",
    "    # 取batch内第0列进行分词，赋给src_words\n",
    "    src_words = [pair[0].split() for pair in batch]\n",
    "    # 取batch内第1列进行分词，赋给trg_words\n",
    "    trg_words = [pair[1].split() for pair in batch]\n",
    "\n",
    "    # paddings 在前在后影响不大，但在后好理解，所以padding_first=False\n",
    "    # [BOS] src [EOS] [PAD] \n",
    "    encoder_inputs, encoder_inputs_mask = tokenizer.encode(\n",
    "        src_words, padding_first=False, add_bos=True, add_eos=True, return_mask=True\n",
    "    )\n",
    "\n",
    "    # 目标语言 [BOS] trg [PAD]\n",
    "    decoder_inputs = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False\n",
    "    )\n",
    "\n",
    "    # 用于后续计算loss\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "    )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device),\n",
    "        \"decoder_labels\": decoder_labels.to(device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device),\n",
    "    }  # 当返回的数据较多时，用dict返回比较合理\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f53f13a987ea9201",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.571687Z",
     "start_time": "2025-02-06T14:04:29.566838Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.008741Z",
     "iopub.status.busy": "2025-02-07T15:37:43.008375Z",
     "iopub.status.idle": "2025-02-07T15:37:43.011461Z",
     "shell.execute_reply": "2025-02-07T15:37:43.011030Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.008721Z"
    }
   },
   "outputs": [],
   "source": [
    "from functools import partial  # 固定collate_fct的tokenizer参数\n",
    "\n",
    "# # 可以调大batch_size,来看最终的bleu，如果GPU内存不够，可以减小batch_size\n",
    "# sampler = TransformerBatchSampler(train_ds, batch_size=256, shuffle_batch=True)\n",
    "# \n",
    "# # batch_sampler 是一个自定义的批次采样器，用于控制如何从数据集中采样批次。与 batch_size 和 shuffle 不同，batch_sampler 可以更灵活地定义批次的组织方式（如按长度分组）。\n",
    "# # partial(collate_fn, tokenizer=tokenizer) 使用 functools.partial 将 tokenizer 参数固定到 collate_fn 中，生成一个新的函数。\n",
    "# sample_dl = DataLoader(train_ds, batch_sampler=sampler, collate_fn=partial(collate_fn, tokenizer=tokenizer))\n",
    "# \n",
    "# for batch in sample_dl:  #外层是拿每个batch\n",
    "#     for key, value in batch.items():  #内层是拿每个batch里面是一个字典\n",
    "#         print(key)\n",
    "#         print(\"-\" * 50)\n",
    "#         print(value)\n",
    "#         print(\"-\" * 50)\n",
    "#     break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa4932b257d82059",
   "metadata": {},
   "source": [
    "# 定义模型\n",
    "\n",
    "- Transformer模型由Embedding、Transformer-Block组成\n",
    "- Embedding包括：\n",
    "    - WordEmbedding\n",
    "    - PositionEmbedding\n",
    "- Transformer-Block包括：\n",
    "    - Self-Attention\n",
    "    - Cross-Attention\n",
    "    - MLP"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d2988138abac64d",
   "metadata": {},
   "source": [
    "## Embedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "847a70137968848a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.819755Z",
     "start_time": "2025-02-06T14:04:29.650702Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.012163Z",
     "iopub.status.busy": "2025-02-07T15:37:43.011983Z",
     "iopub.status.idle": "2025-02-07T15:37:43.164372Z",
     "shell.execute_reply": "2025-02-07T15:37:43.163852Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.012144Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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dVISv4Z43aR2NNrJyxtvvIGsrCxMmzYN69atQ48ePTB48OCTdvdryKxatQoTJkzA6tWrsWzZMlRUVOCiiy5CcXGxqjN58mS8//77ePPNN7Fq1Srs2bMHV111VRBHXbd88803eOGFF9C9e3dTeWO87sOHD+Pcc8+Fw+HAxx9/jJ9//hn//Oc/ERcXp+pUJyV9Q+PRRx/F888/j2effRYbN27Eo48+isceewzPPPOMqtMYrru4uBg9evTA3LlzfR6vzjWOHDkSGzZswLJly/DBBx/giy++wPjx48/UJQREVdddUlKCdevW4YEHHsC6devw9ttvY9OmTbj88stN9RridZNaUqNMSA2Ifv36GRMmTFA/ezweIzU11Zg1a1YQR3V62bdvnwHAWLVqlWEYhpGfn284HA7jzTffVHU2btxoADCys7ODNcw648iRI0b79u2NZcuWGX/84x+NO+64wzCMxnvd9957rzFgwAC/x71er5GcnGw8/vjjqiw/P99wuVzGv//97zMxxNPC0KFDjb/85S+msquuusoYOXKkYRiN87oBGO+88476uTrX+PPPPxsAjG+++UbV+fjjjw2LxWLs3r37jI29Npx43b5Yu3atAcDYsWOHYRiN47pJzWmUKy/l5eXIyckxpea2Wq3IzMxEdnZ2EEd2ejm+WVB8fDwAICcnBxUVFab70KlTJ7Rs2bJR3IcJEyZg6NChpusDGu91v/fee+jbty+uvvpqNG3aFL169cJLL72kjp8qJX1DpX///li+fDl+/fVXAMD333+PL7/8EhdffDGAxnvdOtW5xuzsbMTGxqJv376qTmZmJqxWK9asWXPGx3y6KCgogMViQWxsLIDQuW5ipt7vsBsIBw4cgMfjOWkX3qSkJPzyyy9BGtXpxev1YtKkSTj33HPRtWtXAEBubi6cTqf6JT9OUlIScnNzgzDKumPx4sVYt24dvvnmm5OONdbr/u233/D8888jKysLf/vb3/DNN9/g9ttvh9PpxOjRo9W1+frcN+Trvu+++1BYWIhOnTrBZrPB4/HgoYcewsiRIwGg0V63TnWuMTc3F02bNjUdt9vtiI+PbzT3obS0FPfeey+uu+46lZgxFK6bnEyjnLyEIhMmTMBPP/2EL7/8MthDOe3s2rULd9xxB5YtW3bGs7IGE6/Xi759++Lhhx8GcGy77p9++gnz5s3D6NGjgzy608d//vMfvP7661i0aBHOOussrF+/HpMmTUJqamqjvm5ipqKiAtdccw0Mw8Dzzz8f7OGQINMoHxs1adIENpvtJHdJXl5ejdNuNwQmTpyIDz74AJ9//jmaN2+uypOTk1FeXo78/HxT/YZ+H3JycrBv3z707t0bdrsddrsdq1atwpw5c2C325GUlNQorzslJQVdunQxlXXu3Bk7d+4EYE5Jr9PQr/vuu+/GfffdhxEjRqBbt27485//jMmTJ2PWrFkAGu9161TnGpOTk08yJFRWVuLQoUMN/j4cn7js2LEDy5YtU6suQOO+buKfRjl5cTqd6NOnD5YvX67KvF4vli9fjoyMjCCOrG4xDAMTJ07EO++8gxUrVqB169am43369IHD4TDdh02bNmHnzp0N+j4MGjQIP/74o8pyun79evTt2xcjR45UujFe97nnnnuSFf7XX39Fq1atAJhT0h/neEr6hnzdJSUlsFrNf6psNhu8Xi+AxnvdOtW5xoyMDOTn5yMnJ0fVWbFiBbxeL9LT08/4mOuK4xOXzZs347PPPkNCQoLpeGO9bnIKgh0xfLpYvHix4XK5jFdeecX4+eefjfHjxxuxsbFGbm5usIdWZ9xyyy1GTEyMsXLlSmPv3r3qVVJSourcfPPNRsuWLY0VK1YY3377rZGRkWFkZGQEcdSnB91tZBiN87rXrl1r2O1246GHHjI2b95svP7660Z4eLjxr3/9S9V55JFHjNjYWOPdd981fvjhB+OKK64wWrdubRw9ejSII68do0ePNpo1a2Z88MEHxrZt24y3337baNKkiXHPPfeoOo3huo8cOWJ89913xnfffWcAMJ588knju+++U66a6lzjkCFDjF69ehlr1qwxvvzyS6N9+/bGddddF6xLqhZVXXd5eblx+eWXG82bNzfWr19v+jtXVlam+miI101qR6OdvBiGYTzzzDNGy5YtDafTafTr189YvXp1sIdUpwDw+Vq4cKGqc/ToUePWW2814uLijPDwcOPKK6809u7dG7xBnyZOnLw01ut+//33ja5duxoul8vo1KmT8eKLL5qOe71e44EHHjCSkpIMl8tlDBo0yNi0aVOQRls3FBYWGnfccYfRsmVLw+12G23atDH+/ve/m/7zagzX/fnnn/v8fR49erRhGNW7xoMHDxrXXXedERkZaURHRxtjxowxjhw5EoSrqT5VXfe2bdv8/p37/PPPVR8N8bpJ7bAYhrZNJSGEEEJIPadRxrwQQgghpPHCyQshhBBCGhScvBBCCCGkQcHJCyGEEEIaFJy8EEIIIaRBwckLIYQQQhoUnLwQQgghpEHByQshpFps374dFosF69evD/ZQCCEhDicvhDQQbrzxRlgsFlgsFjgcDiQlJeHCCy/EggULVJ6fujzXsGHD6rRPQgipKzh5IaQBMWTIEOzduxfbt2/Hxx9/jAsuuAB33HEHLr30UlRWVgZ7eIQQckbg5IWQBoTL5UJycjKaNWuG3r17429/+xveffddfPzxx3jllVcAAPn5+bjpppuQmJiI6OhoDBw4EN9//73qY/r06ejZsydeeOEFtGjRAuHh4bjmmmtQUFCgjr/66qt499131UrPypUrVfvffvsNF1xwAcLDw9GjRw9kZ2efyVtACCGcvBDS0Bk4cCB69OiBt99+GwBw9dVXY9++ffj444+Rk5OD3r17Y9CgQTh06JBqs2XLFvznP//B+++/j6VLl+K7777DrbfeCgC46667cM0116hVnr1796J///6q7d///nfcddddWL9+PTp06IDrrruOqz6EkDMKJy+ENAI6deqE7du348svv8TatWvx5ptvom/fvmjfvj2eeOIJxMbG4q233lL1S0tL8dprr6Fnz54477zz8Mwzz2Dx4sXIzc1FZGQkwsLC1CpPcnIynE6nanvXXXdh6NCh6NChA2bMmIEdO3Zgy5YtwbhsQkiIwskLIY0AwzBgsVjw/fffo6ioCAkJCYiMjFSvbdu2YevWrap+y5Yt0axZM/VzRkYGvF4vNm3adMpzde/eXemUlBQAwL59++rwagghpGrswR4AIaT2bNy4Ea1bt0ZRURFSUlJMMSrHiY2NrZNzORwOpS0WCwDUuduJEEKqgpMXQho4K1aswI8//ojJkyejefPmyM3Nhd1uR1pamt82O3fuxJ49e5CamgoAWL16NaxWKzp27AgAcDqd8Hg8Z2L4hBBSYzh5IaQBUVZWhtzcXHg8HuTl5WHp0qWYNWsWLr30UowaNQpWqxUZGRkYNmwYHnvsMXTo0AF79uzBhx9+iCuvvBJ9+/YFALjdbowePRpPPPEECgsLcfvtt+Oaa65BcnIyACAtLQ2ffPIJNm3ahISEBMTExATzsgkhxAQnL4Q0IJYuXYqUlBTY7XbExcWhR48emDNnDkaPHg2r9VgI20cffYS///3vGDNmDPbv34/k5GScd955SEpKUv20a9cOV111FS655BIcOnQIl156KZ577jl1fNy4cVi5ciX69u2LoqIifP7551Wu5BBCyJnEYhiGEexBEELOHNOnT8eSJUu4zT8hpMFCtxEhhBBCGhScvBBCCCGkQcHHRoQQQghpUHDlhRBCCCENCk5eCCGEENKg4OSFEEIIIQ0KTl4IIYQQ0qDg5IUQQgghDQpOXgghhBDSoODkhRBCCCENCk5eCCGEENKg4OSFEEIIIQ2K/wcnumZWHU5nbgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "class TransformerEmbedding(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.hidden_size = config[\"d_model\"]  # 词向量维度\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "\n",
    "        # layers,设置padding_idx可以让pad的词向量全为0\n",
    "        self.word_embedding = nn.Embedding(\n",
    "            self.vocab_size, self.hidden_size, padding_idx=self.pad_idx\n",
    "        )\n",
    "        self.position_embedding = nn.Embedding(\n",
    "            self.max_length, self.hidden_size,\n",
    "            # 位置编码，权重通过get_positional_encoding函数计算得到\n",
    "            _weight=self.get_position_encoding(self.max_length, self.hidden_size)\n",
    "        )\n",
    "\n",
    "        self.position_embedding.weight.requires_grad_(False)  # 不更新位置编码的权重\n",
    "        self.dropout = nn.Dropout(dropout_rate)  # 随机失活层\n",
    "\n",
    "    def get_word_embedding_weight(self):\n",
    "        return self.word_embedding.weight\n",
    "\n",
    "    # 计算位置信息\n",
    "    # 用于定义类方法（class method）。\n",
    "    # 类方法是绑定到类而不是实例的方法，可以通过类本身或类的实例调用。\n",
    "    @classmethod\n",
    "    def get_position_encoding(self, max_length, hidden_size):\n",
    "        # max_length是最大长度，hidden_size是embedding维度相等\n",
    "        pe = torch.zeros(max_length, hidden_size)  # 初始化位置编码\n",
    "        # .unsqueeze(1) 是将这个一维张量转换为二维张量，\n",
    "        # 即将其形状从 (max_length,) 变为 (max_length, 1)。\n",
    "        # 这个操作在张量的维度上增加了一个维度，使其从一维变为二维，第二维的大小为 1。\n",
    "        position = torch.arange(0, max_length).unsqueeze(1)  # 位置信息,从0到max_length-1\n",
    "        # 计算位置编码的权重,为了性能考量（是数学上的对数函数分解），这里用了log函数\n",
    "        # torch.arange(0, hidden_size, 2) 在 PyTorch 中用于创建一个一维张量，包含从 0 开始到（但不包括）hidden_size 的数值，步长为 2\n",
    "        div_term = torch.exp(\n",
    "            torch.arange(0, hidden_size, 2) *\n",
    "            -(torch.log(torch.Tensor([10000.0])) / hidden_size)\n",
    "        )\n",
    "        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数位置\n",
    "        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数位置\n",
    "        return pe\n",
    "\n",
    "    def forward(self, input_ids):\n",
    "        # input_ids: [batch_size,seq_len]\n",
    "        seq_len = input_ids.shape[1]\n",
    "        assert (\n",
    "                seq_len <= self.max_length), f\"input sequence length should no more than {self.max_length} but got {seq_len}\"\n",
    "\n",
    "        position_ids = torch.arange(seq_len, dtype=torch.long, device=input_ids.device)  # 位置信息\n",
    "        # unsqueeze(0): 在第一维添加一个维度，使张量的形状从 [seq_len] 变为 [1, seq_len]\n",
    "        # expand_as(input_ids): 扩展张量的形状，使其与 input_ids 张量的形状相同\n",
    "        position_ids = position_ids.unsqueeze(0).expand_as(input_ids)\n",
    "        # position_ids: [batch_size,seq_len]\n",
    "\n",
    "        # print(input_ids.shape) #为了调试\n",
    "        # print(position_ids.shape) #为了调试\n",
    "        # print(position_ids) #为了调试\n",
    "\n",
    "        # embedding层\n",
    "        word_embeds = self.word_embedding(input_ids)  # 词嵌入\n",
    "        # word_embeds: [batch_size,seq_len,hidden_size]\n",
    "        # position_ids: [batch_size,seq_len]\n",
    "        pos_embeds = self.position_embedding(position_ids)  # 位置嵌入 # ？\n",
    "        # pos_embeds: [batch_size,seq_len,hidden_size]   \n",
    "        embeds = word_embeds + pos_embeds  # 相加得到嵌入向量\n",
    "        # embeds: [batch_size,seq_len,hidden_size]\n",
    "        embeds = self.dropout(embeds)  # 随机失活\n",
    "        return embeds\n",
    "\n",
    "\n",
    "# #随机input，调用TransformerEmbedding\n",
    "# config={\n",
    "#     \"vocab_size\": 100,\n",
    "#     \"d_model\": 128,\n",
    "#     \"pad_idx\": 0,\n",
    "#     \"max_length\": 64,\n",
    "#     \"dropout\": 0.1,\n",
    "# }\n",
    "# input_ids = torch.randint(0, 100, (2, 50))\n",
    "# embeds = TransformerEmbedding(config)(input_ids)\n",
    "# embeds.shape\n",
    "\n",
    "# 绘制位置编码\n",
    "def plot_position_embedding(position_embedding):\n",
    "    plt.pcolormesh(position_embedding)  # 绘制位置编码矩阵\n",
    "    plt.xlabel('Depth')\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar()  # 颜色条，-1到1的颜色范围\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "position_embedding = TransformerEmbedding.get_position_encoding(64, 128)\n",
    "plot_position_embedding(position_embedding)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39630e37b910bd7f",
   "metadata": {},
   "source": [
    "## Transformer Block"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72e7edad4645f763",
   "metadata": {},
   "source": [
    "### scaled-dot-product-attention"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "7bd08ede5d628f45",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.834753Z",
     "start_time": "2025-02-06T14:04:29.821751Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.167594Z",
     "iopub.status.busy": "2025-02-07T15:37:43.167397Z",
     "iopub.status.idle": "2025-02-07T15:37:43.266045Z",
     "shell.execute_reply": "2025-02-07T15:37:43.265435Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.167571Z"
    }
   },
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional, Tuple\n",
    "\n",
    "Tensor = torch.Tensor\n",
    "\n",
    "\n",
    "# 修饰器 @dataclass 会自动生成 __init__、__repr__ 和 __eq__ 等方法，减少了样板代码的编写\n",
    "@dataclass\n",
    "class AttentionOutput:\n",
    "    hidden_states: Tensor\n",
    "    attn_scores: Tensor\n",
    "\n",
    "\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]  # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"]  # 多头注意力的头数\n",
    "        assert (\n",
    "                self.hidden_size % self.num_heads == 0\n",
    "        ), \"Hidden size must be divisible by num_heads but got {} and {}\".format(\n",
    "            self.hidden_size, self.num_heads)\n",
    "        self.head_dim = self.hidden_size // self.num_heads  # 每个头的维度\n",
    "\n",
    "        # layers\n",
    "        # 第二个self.hidden_size可以*系数\n",
    "        self.Wq = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wk = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wv = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wo = nn.Linear(self.hidden_size, self.hidden_size, bias=False)  # 输出层\n",
    "\n",
    "    # -> Tensor 是一种类型注解，表示函数的返回值类型是 Tensor\n",
    "    def _split_heads(self, x: Tensor) -> Tensor:\n",
    "        # 若hidden_size是512\n",
    "        bs, seq_len, _ = x.shape  # [batch_size,seq_len,hidden_size]\n",
    "        # 先将x变形为[batch_size,seq_len,num_heads,head_dim]\n",
    "        x = x.view(bs, seq_len, self.num_heads, self.head_dim)\n",
    "        # num_heads是8，head_dim是64\n",
    "        return x.permute(0, 2, 1, 3)\n",
    "        # 变换维度，[batch_size, num_heads, seq_len, head_dim]\n",
    "\n",
    "    def _merge_heads(self, x: Tensor) -> Tensor:\n",
    "        # 将多头注意力的输出合并为一个张量\n",
    "        bs, _, seq_len, _ = x.shape  # [batch_size, num_heads, seq_len, head_dim]\n",
    "        # 变换维度，变为[batch_size, seq_len, hidden_size]\n",
    "        return x.permute(0, 2, 1, 3).reshape(bs, seq_len, self.hidden_size)\n",
    "\n",
    "    def forward(self, querys, keys, values, attn_mask=None) -> AttentionOutput:\n",
    "        # split heads\n",
    "        # [batch_size, seq_len,hidden_dim]->[batch_size, num_heads, seq_len, head_dim]\n",
    "        querys = self._split_heads(self.Wq(querys))\n",
    "        keys = self._split_heads(self.Wk(keys))\n",
    "        values = self._split_heads(self.Wv(values))\n",
    "\n",
    "        # calculate attention scores\n",
    "        # 计算注意力分数，matmul是矩阵乘法(在后两个维度上进行)，mT是矩阵转置,\n",
    "        # qk_logits是[batch_size, num_heads, seq_len, seq_len]\n",
    "        qk_logits = torch.matmul(querys, keys.mT)\n",
    "        if attn_mask is not None:\n",
    "            # seq_len*seq_len的部分是有效的\n",
    "            attn_mask = attn_mask[:, :, :querys.shape[-2], :keys.shape[-2]]  #切片\n",
    "            qk_logits += attn_mask * -1e9  # 给需要mask的地方设置一个负无穷\n",
    "        # 计算注意力数,softmax是在seq_len维度上进行的\n",
    "        attn_scores = F.softmax(qk_logits / (self.head_dim ** 0.5), dim=-1)\n",
    "        # attn_scores: [batch_size, num_heads, seq_len, seq_len]\n",
    "\n",
    "        # softmax后的结果与value相乘，得到新的表示\n",
    "        embeds = torch.matmul(attn_scores, values)\n",
    "        # embeds的尺寸是[batch_size, num_heads, seq_len, head_dim]\n",
    "        # _merge_heads(embeds)的输出是[batch_size, seq_len, hidden_size]\n",
    "        embeds = self.Wo(self._merge_heads(embeds))  # 输出层\n",
    "        # embeds: [batch_size, seq_len, hidden_size]\n",
    "\n",
    "        return AttentionOutput(hidden_states=embeds, attn_scores=attn_scores)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a9eb177f33d1050d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.843757Z",
     "start_time": "2025-02-06T14:04:29.835755Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.267090Z",
     "iopub.status.busy": "2025-02-07T15:37:43.266642Z",
     "iopub.status.idle": "2025-02-07T15:37:43.272573Z",
     "shell.execute_reply": "2025-02-07T15:37:43.272039Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.267067Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key_value.shape torch.Size([2, 4, 2])\n",
      "torch.Size([2, 3, 2])\n",
      "torch.Size([2, 2, 3, 4])\n"
     ]
    }
   ],
   "source": [
    "mha = MultiHeadAttention({\"num_heads\": 2, \"d_model\": 2})\n",
    "query = torch.randn(2, 3, 2)  # [batch_size, seq_len, hidden_size]\n",
    "query /= query.norm(dim=-1, keepdim=True)  # 归一化\n",
    "key_value = torch.randn(2, 4, 2)\n",
    "print(f'key_value.shape {key_value.shape}')\n",
    "outputs = mha(query, key_value, key_value)  #最终输出shape和query的shape一样\n",
    "print(outputs.hidden_states.shape)\n",
    "print(outputs.attn_scores.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e37f29d98cf51bac",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:29.852319Z",
     "start_time": "2025-02-06T14:04:29.845757Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.273551Z",
     "iopub.status.busy": "2025-02-07T15:37:43.273210Z",
     "iopub.status.idle": "2025-02-07T15:37:43.278867Z",
     "shell.execute_reply": "2025-02-07T15:37:43.278341Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.273527Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.0411, 0.2287, 0.7302],\n",
      "        [0.6804, 0.0501, 0.2695]])\n"
     ]
    }
   ],
   "source": [
    "#随机一个2阶张量，在-1维度计算softmax\n",
    "import torch.nn.functional as F\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "x = torch.randn(2, 3)\n",
    "x_softmax = F.softmax(x, dim=-1)\n",
    "print(x_softmax)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "b451e28eb10aaf26",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.232694Z",
     "start_time": "2025-02-06T14:04:30.002057Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.279844Z",
     "iopub.status.busy": "2025-02-07T15:37:43.279510Z",
     "iopub.status.idle": "2025-02-07T15:37:43.520390Z",
     "shell.execute_reply": "2025-02-07T15:37:43.519865Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.279824Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhUAAAG6CAYAAAC/RrTYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAphpJREFUeJzs3Xd8U9X7wPHPTdK9S0sRKHuvMspeMgSRDTIUZIlsF6II+hNwgagIKm4BQREEBJUpIHvIkt0WWqAFSvfebXJ/fxRSQlMskDTS7/N+vfLS3p578pzcm6dPzj03KKqqqgghhBBCPCCNrQMQQgghROkgRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhbCqUaNGUaVKlWK1nT17Noqi/Gu7Rx99lAYNGjxgZJZVpUoVRo0aZeswSsy9HNf/guKeW7e3jYuLs3JUpYu8bgKkqBAlLCMjg9mzZ7N7925bh/Kf9v7777Nhw4ZC2w8ePMjs2bNJSkqyegyRkZHMnj2bkydPWv25bKGo19iWzp8/z+zZs7ly5YqtQxHivkhRIUpURkYGc+bMMVtUvPnmm2RmZpZ8UP9Bdysq5syZU2JFxZw5c8wWFd9++y0hISFWj8FSzJ1b/9WiYs6cOVJUiIeWztYBCHGLTqdDp5NT8mFgZ2dn6xDuiZxbQpQMman4H3Tr2ueFCxcYPnw4Hh4e+Pr68n//93+oqsrVq1fp27cv7u7ulCtXjo8//thk/2XLlqEoSqFPU7t370ZRlCIvbVy5cgVfX18A5syZg6IoKIrC7NmzTeIqrvPnz9OpUyecnZ2pUKEC8+fPL9QmOzubWbNmUaNGDRwcHPD39+e1114jOzvbpN3SpUvp3LkzZcuWxcHBgXr16vHll18W6k9VVd59910qVqyIs7MznTp14ty5c8WO+aOPPqJNmzaUKVMGJycnmjVrxtq1a03aKIpCeno6P/zwg/E1GjVqFLNnz+bVV18FoGrVqsbf3X4cfvzxR5o1a4aTkxPe3t4MHTqUq1evmvR/a03K3V6/3bt307x5cwBGjx5tfK5ly5YB5tdUpKen88orr+Dv74+DgwO1a9fmo48+4s5/CFlRFKZMmcKGDRto0KABDg4O1K9fn61bt971tVNVFR8fH6ZOnWrcZjAY8PT0RKvVmszefPDBB+h0OtLS0oDC51ZRr/HtkpKSGDVqFJ6ennh4eDB69GgyMjJM2uTl5fHOO+9QvXp1HBwcqFKlCjNnzix0ft1+nt/u9rU4y5YtY9CgQQB06tTJGNfdLhWOGjUKV1dXIiIi6NWrF66urlSoUIHFixcDcObMGTp37oyLiwuVK1dm5cqVJvsnJCQwbdo0GjZsiKurK+7u7vTo0YNTp04Veq7PPvuM+vXr4+zsjJeXF4GBgYX6u1N4eDg1atSgQYMGREdH37WtKB2kqPgfNmTIEAwGA/PmzaNly5a8++67LFy4kMcee4wKFSrwwQcfUKNGDaZNm8bevXsf+Pl8fX2Nf6j79+/PihUrWLFiBQMGDLjnvhITE3n88ccJCAjg448/pk6dOkyfPp0tW7YY2xgMBvr06cNHH31E7969+eyzz+jXrx+ffPIJQ4YMMenvyy+/pHLlysycOZOPP/4Yf39/Jk2aZEzOt7z11lv83//9HwEBAXz44YdUq1aNbt26kZ6eXqy4Fy1aRJMmTXj77bd5//330el0DBo0iE2bNhnbrFixAgcHB9q3b298jcaPH8+AAQN46qmnAPjkk0+Mv7tVqL333nuMGDGCmjVrsmDBAl566SV27txJhw4dCl0u+bfXr27durz99tsAjBs3zvhcHTp0MDsuVVXp06cPn3zyCY8//jgLFiygdu3avPrqqyZFwC379+9n0qRJDB06lPnz55OVlcXAgQOJj48v8rVTFIW2bduanIunT58mOTkZgAMHDhi379u3jyZNmuDq6mq2r6Je49sNHjyY1NRU5s6dy+DBg1m2bBlz5swxaTN27FjeeustmjZtyieffELHjh2ZO3cuQ4cOLXIcRenQoQMvvPACADNnzjTGVbdu3bvup9fr6dGjB/7+/syfP58qVaowZcoUli1bxuOPP05gYCAffPABbm5ujBgxgsuXLxv3vXTpEhs2bKBXr14sWLCAV199lTNnztCxY0ciIyON7b799lteeOEF6tWrx8KFC5kzZw6NGzfm77//LjKusLAwOnTogJubG7t378bPz++eXxPxEFLF/5xZs2apgDpu3Djjtry8PLVixYqqoijqvHnzjNsTExNVJycndeTIkcZtS5cuVQH18uXLJv3u2rVLBdRdu3YZt40cOVKtXLmy8efY2FgVUGfNmlVkXP+mY8eOKqAuX77cuC07O1stV66cOnDgQOO2FStWqBqNRt23b5/J/l999ZUKqAcOHDBuy8jIKPQ83bt3V6tVq2b8OSYmRrW3t1d79uypGgwG4/aZM2eqgMlrVJQ7nycnJ0dt0KCB2rlzZ5PtLi4uZvv78MMPzb72V65cUbVarfree++ZbD9z5oyq0+lMthf39Tt69KgKqEuXLi0Ux53HdcOGDSqgvvvuuybtnnzySVVRFDU0NNS4DVDt7e1Ntp06dUoF1M8++6zQc905fq1Wq6akpKiqqqqffvqpWrlyZbVFixbq9OnTVVVVVb1er3p6eqovv/yycT9z51ZRr/GttmPGjDHZ3r9/f7VMmTLGn0+ePKkC6tixY03aTZs2TQXUv/76y2TM5s75ypUrm8SwZs2aQu+huxk5cqQKqO+//75x2633rKIo6qpVq4zbg4ODC8WRlZWl6vV6kz4vX76sOjg4qG+//bZxW9++fdX69evfNZZbr1tsbKwaFBSkli9fXm3evLmakJBQrLGI0kFmKv6HjR071vj/Wq2WwMBAVFXl2WefNW739PSkdu3aXLp0yRYhFsnV1ZXhw4cbf7a3t6dFixYmca5Zs4a6detSp04d4uLijI/OnTsDsGvXLmNbJycn4/8nJycTFxdHx44duXTpkvGT8I4dO8jJyeH55583mUp/6aWXih337c+TmJhIcnIy7du358SJE8UfvBm//vorBoOBwYMHm4y1XLly1KxZ02SsULzX715s3rwZrVZr/KR9yyuvvIKqqiYzSABdu3alevXqxp8bNWqEu7v7vz5/+/bt0ev1HDx4EMifkWjfvj3t27dn3759AJw9e5akpCTat29/X2O5ZcKECYWeOz4+npSUFCB/zEChmZhXXnkFwGT2ydpufy/fes+6uLgwePBg4/batWvj6elp8ho7ODig0eT/GdDr9cTHx+Pq6krt2rVNzklPT0+uXbvG0aNH/zWWs2fP0rFjR6pUqcKOHTvw8vKyxBDFQ0KKiv9hlSpVMvnZw8MDR0dHfHx8Cm1PTEwsydD+VcWKFQutv/Dy8jKJ8+LFi5w7dw5fX1+TR61atQCIiYkxtj1w4ABdu3bFxcUFT09PfH19mTlzJoCxqAgPDwegZs2aJs/r6+tb7MS5ceNGWrVqhaOjI97e3sZLQree435dvHgRVVWpWbNmofEGBQWZjBWK9/rdi/DwcMqXL4+bm5vJ9ltT97deu1vuPPeK+/xNmzbF2dnZWEDcKio6dOjAsWPHyMrKMv6uXbt29zWWomK8dYxvxRgeHo5Go6FGjRom7cqVK4enp2ehMVuLo6Oj8RLYLR4eHmaP8Z3vZYPBwCeffELNmjVxcHDAx8cHX19fk8tKANOnT8fV1ZUWLVpQs2ZNJk+ebHK56Xa9e/fGzc2Nbdu24e7ubsGRioeBLIf+H6bVaou1DTBZbFfUYkq9Xm+ZwIqhOHEaDAYaNmzIggULzLb19/cH8q/9dunShTp16rBgwQL8/f2xt7dn8+bNfPLJJxgMBovEvG/fPvr06UOHDh344osveOSRR7Czs2Pp0qX/uuDt3xgMBhRFYcuWLWZfmzvXFhTn9bOm+31+Ozs7WrZsyd69ewkNDSUqKor27dvj5+dHbm4uf//9N/v27aNOnTqF/tBaK8Z7WVx8J0u8Z4qKszjxv//++/zf//0fY8aM4Z133sHb2xuNRsNLL71kct7XrVuXkJAQNm7cyNatW1m3bh1ffPEFb731VqF1JgMHDuSHH37gp59+KrRORZR+UlSIe3brE9udi/+K88nsQRLwvapevTqnTp2iS5cud33eP/74g+zsbH7//XeTT6d3XjKoXLkykD8rUK1aNeP22NjYYn3CX7duHY6Ojmzbtg0HBwfj9qVLlxZqW1S8RW2vXr06qqpStWpV40zMg7qXY1W5cmV27NhBamqqyWxFcHCw8feW0r59ez744AN27NiBj48PderUQVEU6tevz759+9i3bx+9evX6134e9FysXLkyBoOBixcvmiymjI6OJikpyWTMXl5ehd4vOTk53Lhxw6Ix3au1a9fSqVMnvv/+e5PtSUlJhWYsXVxcGDJkCEOGDCEnJ4cBAwbw3nvvMWPGDBwdHY3tPvzwQ3Q6HZMmTcLNzY2nn366RMYi/hvk8oe4Z7euhd++Cl+v1/PNN9/8677Ozs5A4YLEGgYPHsz169f59ttvC/0uMzPTeMfGrU90t3+CS05OLvTHvmvXrtjZ2fHZZ5+ZtF24cGGx4tFqtSiKYvLp9MqVK2a/gMnFxcXsa+Ti4gIUfv0GDBiAVqtlzpw5hT5Jq6p617sqilLUc5nzxBNPoNfr+fzzz022f/LJJyiKQo8ePe75+YvSvn17srOzWbhwIe3atTP+Ib51J0dkZGSx1lMU9RoX1xNPPAEUPv63ZsZ69uxp3Fa9evVCd1B98803hWYq7uU1twStVlvofFmzZg3Xr1832Xbn+WNvb0+9evVQVZXc3FyT3ymKwjfffMOTTz7JyJEj+f33360TvPhPkpkKcc/q169Pq1atmDFjBgkJCXh7e7Nq1Sry8vL+dV8nJyfq1avH6tWrqVWrFt7e3jRo0MAq/5bHM888wy+//MKECRPYtWsXbdu2Ra/XExwczC+//MK2bdsIDAykW7du2Nvb07t3b8aPH09aWhrffvstZcuWNfkk6evry7Rp05g7dy69evXiiSee4J9//mHLli2FPtWZ07NnTxYsWMDjjz/O008/TUxMDIsXL6ZGjRqcPn3apG2zZs3YsWMHCxYsoHz58lStWpWWLVvSrFkzAN544w2GDh2KnZ0dvXv3pnr16rz77rvMmDGDK1eu0K9fP9zc3Lh8+TLr169n3LhxTJs27Z5ev+rVq+Pp6clXX32Fm5sbLi4utGzZkqpVqxZq27t3bzp16sQbb7zBlStXCAgI4M8//+S3337jpZdeMlmU+aBat26NTqcjJCSEcePGGbd36NDBeMtycYqKol7j4goICGDkyJF88803JCUl0bFjR44cOcIPP/xAv3796NSpk7Ht2LFjmTBhAgMHDuSxxx7j1KlTbNu2rdB507hxY7RaLR988AHJyck4ODgYvz/FGnr16sXbb7/N6NGjadOmDWfOnOGnn34ymYkD6NatG+XKlaNt27b4+fkRFBTE559/Ts+ePQutowHQaDT8+OOP9OvXj8GDB7N582bjAmlRypX8DSfC1m6/9et2I0eOVF1cXAq179ixY6HbycLCwtSuXbuqDg4Oqp+fnzpz5kx1+/bt/3pLqaqq6sGDB9VmzZqp9vb2Jre43cstpeZubzP3XDk5OeoHH3yg1q9fX3VwcFC9vLzUZs2aqXPmzFGTk5ON7X7//Xe1UaNGqqOjo1qlShX1gw8+UJcsWVLo9k29Xq/OmTNHfeSRR1QnJyf10UcfVc+ePVvo1sCifP/992rNmjVVBwcHtU6dOurSpUvNjjs4OFjt0KGD6uTkVOh21XfeeUetUKGCqtFoCsW3bt06tV27dqqLi4vq4uKi1qlTR508ebIaEhJyX6/fb7/9ptarV0/V6XQmt5eaa5uamqq+/PLLavny5VU7Ozu1Zs2a6ocffmhy+62q5t9eOXny5ELPX9zXUFVVtXnz5iqg/v3338Zt165dUwHV39+/UPt7eY2Len+Yu5U6NzdXnTNnjlq1alXVzs5O9ff3V2fMmKFmZWWZ7KvX69Xp06erPj4+qrOzs9q9e3c1NDTU7Ji//fZbtVq1aqpWq/3X20vv5T2rqvmvcc+ePY0/Z2Vlqa+88orxfG7btq166NAhtWPHjmrHjh2N7b7++mu1Q4cOapkyZVQHBwe1evXq6quvvmryHjL3umVkZKgdO3ZUXV1d1cOHDxc5DlF6KKpaQiuzhBBCCFGqyZoKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwiFJdVCxevJgqVarg6OhIy5YtOXLkiK1Dspi9e/fSu3dvypcvj6IoZv+ly4fV3Llzad68OW5ubpQtW5Z+/foREhJi67As5ssvv6RRo0a4u7vj7u5O69at2bJli63DErcprbmjNOcNkNzxX1Bqi4rVq1czdepUZs2axYkTJwgICKB79+7ExMTYOjSLSE9PJyAggMWLF9s6FIvbs2cPkydP5vDhw2zfvp3c3Fy6detm/KfKH3YVK1Zk3rx5HD9+nGPHjtG5c2f69u3LuXPnbB2aoHTnjtKcN0Byx3+Crf9FM2tp0aKFyb+EqNfr1fLly6tz5861YVTWAajr16+3dRhWExMTowLqnj17bB2K1Xh5eanfffedrcMQ6v9O7ijteUNVJXfYQqmcqcjJyeH48eN07drVuE2j0dC1a1cOHTpkw8jE/UhOTgbA29vbxpFYnl6vZ9WqVaSnp9O6dWtbh/M/T3JH6SK5o+TpbB2ANcTFxaHX6/Hz8zPZ7ufnR3BwsI2iEvfDYDDw0ksv0bZtWxo0aGDrcCzmzJkztG7dmqysLFxdXVm/fj316tWzdVj/8yR3lB6SO2yjVBYVovSYPHkyZ8+eZf/+/bYOxaJq167NyZMnSU5OZu3atYwcOZI9e/b8p5KDEA8zyR22USqLCh8fH7RaLdHR0Sbbo6OjKVeunI2iEvdqypQpbNy4kb1791KxYkVbh2NR9vb21KhRA4BmzZpx9OhRFi1axNdff23jyP63Se4oHSR32E6pXFNhb29Ps2bN2Llzp3GbwWBg586d/6lrT8I8VVWZMmUK69ev56+//qJq1aq2DsnqDAYD2dnZtg7jf57kjoeb5A7bK5UzFQBTp05l5MiRBAYG0qJFCxYuXEh6ejqjR4+2dWgWkZaWRmhoqPHny5cvc/LkSby9valUqZINI3twkydPZuXKlfz222+4ubkRFRUFgIeHB05OTjaO7sHNmDGDHj16UKlSJVJTU1m5ciW7d+9m27Zttg5NULpzR2nOGyC54z/B1refWNNnn32mVqpUSbW3t1dbtGihHj582NYhWcyuXbtUoNBj5MiRtg7tgZkbF6AuXbrU1qFZxJgxY9TKlSur9vb2qq+vr9qlSxf1zz//tHVY4jalNXeU5ryhqpI7/gsUVVXVkixihBBCCFE6lco1FUIIIYQoeVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWESpLyqys7OZPXv2f+prTC1JxvdwK+3je1iV9uMi43u4/ZfHV+q//ColJQUPDw+Sk5Nxd3e3dTgWJ+N7uJX28T2sSvtxkfE93P7L4yv1MxVCCCGEKBlSVAghhBDCIkr8Xyk1GAxERkbi5uaGoihWf76UlBST/5Y2Mr6HW0mPT1VVUlNTKV++PBrNw/OZQvKGZcn4Hm7/5bxR4msqrl27hr+/f0k+pRDiDlevXqVixYq2DqPYJG8IYXvFyRslPlPh5uYGQN0R/4fW3rGkn75E+Hx3xNYhWNX6C2dsHYJVNdw+wtYhWI0hM4vIafOM78OHxa14a45/q9TmjUpPXLF1CFaV+HXpLgpvdDbYOgSrMWRmETl9brHyRokXFbemLrX2jqU2OegUO1uHYFXubg/PtPn90DiVzvPydiVxCcGSTPKGQ+k8PnYu9rYOwap0dqXzuN2icSq9RcUtxckbpfuvgxBCCCFKjBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwCCkqhBBCCGEROlsHcK+GdAhgZNdAyri7cOF6LB/8souz4VFm2w5o05BeLetSo7wPAOcjovn89wNFtn9jaBcGtQ/gw7W7+GnXP1Ybw930mdSdQdP64F3Ok7BT4Sx+YQkhR0PNtu0xtguPPdORKg38Abh4/BJL3vi5UPuRc4bQY2wXXD1dOHcgmE8nfcv1UPOvgdU5D0NxGQsaX8gNRk19G3JPm2/r0A3FdQJoKwM60Iejpn8PWb+ZNFNcXwSnwaBxh5zjqCmzQB9u/bGYMaJOE8Y1aImvkwtBiTHMOryDU3E3zLYdWiuAgdXrU9vLF4Az8VHMP763UPsaHmV4PbAjLctVQqcoXEyKZ8Ku9USmp1p9PKXJ0DYBjO7YDB83F0JuxPL+hl2cvRpttu3AFg3o06weNcqVAeD89RgWbdlfZPu3BnRhcOtGzPttNz/ut03u6PlIewZU7IKXvTuX067zddhaLqSZfx90L9eGzmVbUNn5EQBC066y/MofJu097dwYVbUvTTzr4KJz4lxyKF+HrSUyK7ZExnOnAd0bM6x3c7w9XQgNj2XBkp0EhZnPY326NOTxDvWp5p+f+0MuRfPVz/uM7bVaDeOHtqN1k6qUL+tJWkY2x86E8+XKvcQlppfYmG43om4TxjVqkZ87EmKYdWgHp2LNj29o7UYMrHlb7oiLYv6xvYXa1/D05vXmj9LyEf+C3LFjg1Vzx33NVCxevJgqVarg6OhIy5YtOXLkiKXjMqtb01q8MqAjX28+zFPzfuTCtVi+mDIAL1cns+0Da1Vk67EQnlu0hhEf/Ux0YipfThlAWQ/XQm07BdSgUdVHiElKs/YwitRxcBvGfzySH99ew8Rm07l0Opy5W9/A09fdbPuAjvXZtWo/r3aew4tt3iD2ajzztr1JmfLexjZDXutLv+d7sGjiNzzfagZZ6dnM3fomdg52JTWsAo5PoLjNRE37HDWuH+QFoXgtAY23+fZqEmral6jxg1Hje6NmrkPxmAf27QrauIwD5xGoKW+hxj8JaiaK11LAviRGZKJX1Tq82aIzi04eoNfvywhKiGFFt8GUcXQ22751OX9+vxzE0K0/03/TCiLTU1nRbTB+zgXnZyU3T9Y+MYyw5ASGbllJ99+W8umpg2Tr9SU1LIuyVe54PKAWr/XuwJfbDzNo4U+ERMbx9dgBeLuYzx3Nq1dk88lgxny9luGfryIqKZVvnhtAWXeXQm27NKhOo8rliE62Xe5o79OUsdX683PEFl78Zz6X06/zdoNJeNgVznUADT1qsCf2ODPOfMq0UwuIzU7k7YaTKGPvYWzzZr3nKOdYhnfPf8OL/3xATHYC7zacgoOm5N9bXVrX5oURj7Jk7SFGT19BaHgMn7zxJF7u5t9bTer5s+NAMM/PWc34N1cSE5/KwjefxMcr//VwtNdRq2pZlq47zOjpy5n58W9UKu/NB6/1L8lhGfWqVoc3W3Vi0YkD9NrwA0EJsax4/C6545FK/B4WxNBNq+j/+4/5ueNxM7mj1zDCkuMZuulnuv+6jE//OWT13HHPRcXq1auZOnUqs2bN4sSJEwQEBNC9e3diYmKsEZ+JZ7o049eDZ/nt8DkuRSXw7qodZOXk0a91A7PtZy7bwi/7ThFyLZYr0YnM+Wk7iqLQora/SbuyHq68PqgTM5dtIc+GyXrgy73Y8t1Oti3bTUTQNRZN+IbsjBy6j+lstv28Zz7ljy//JOzUFa6GRLLgua9QNApNuhS8Hv1f7MlP763j0O/HuHwmgg9Gfk6Z8l607de8pIZlpDiPgYzVkLkO9KGoKW+BmglOT5rfIecIZG8HfRjoIyDjB8gLQbEPvK3PkahpX0D2TsgLQU1+FbRlwfGxEhpVgbH1m7PqwinWhJ7hYnI8Mw9uIzMvl8E1G5pt/+LejawI/ofzCTGEJScw/cAWNIpC20cqG9u82rQDu66FMffYbs4lxBCRmsSOq6HEZ2WU1LAsxpa5Y0SHpqz9+ywbjp3nUkwCb/+6g6zcPPq3MJ87Xv95K6sPnSYkMpbLsYnMWrMdjaLQqmYlk3Zl3V2Y0bcT01dutWnu6FehE9uiDrEj+m+uZkSxOHQ12YYcHvNrbbb9RyHL2XxjH5fTr3MtM5rPLq5Eg0KAZ20Ayjv5Use9Kl+EruZiWgTXM2P4IvQX7DV2dPRtVpJDA2Bor0B+33mGTbvPcuV6PPO/3U52Ti69Opk/fnM+28yvf57kYngs4ZEJzP1qGxpFIbBh/vFLz8zhpXfX8tehECJuJHLu4g0WLNlJ3erl8CvjVpJDA2Bsg0BWBZ9mzcWzXEyKZ+b+m7mjVhG5Y/dGVgSdLMgd+7bm547yt+WOwPbsunqJuUf2cC7+Zu6IsH7uuOeiYsGCBTz33HOMHj2aevXq8dVXX+Hs7MySJUusEZ+RTquhrr8ffwcXTM+pKvwdHE6jao8Uqw9Hex06rZbkjCzjNkWBd0c+zg87jhF2I97icReXzk5HrWbVOLGj4FKAqqqc2HGaeq1qFasPB2d7dHY6UhPyPzGVq1qWMo948c+OM8Y2GSkZBP8dSr3WtS07gH9lB3b1UXMO3rZNhZyDKHZNiteFfWvQVkXNOZr/s9YfRVsWbu9TTYPcU8Xv00LsNBoalinH/sjbzk9g/40rNC1boVh9OGntsNNoSMrOPz8VoLN/NS6nJLK822COD53Chl7P0K1STSuMwPpsmTvqVfDj8MUI4zZVhcMXIwio/GC5Y+5Tj7Nsz3HCom2YOxQtNdz8OZkUYtymonIyKYQ67lWK1YeD1h6toiU1L3/q307JvzKeY8gz6TNXzaOeR3XLBV8MOq2G2tX8OHbGNPcfPRNBg1rli9WHo4MOnU5DSlpWkW1cnO0xGFRSM7IfOOZ7YafR0NCnHPsjrxi3qcD+6+E09Sve+Jx05nJHdS4nJ7D88UEcHzaZDX2G061yDcsP4A73VFTk5ORw/PhxunbtWtCBRkPXrl05dOiQxYO7nZerEzqthvhU0yorPjUDHzNTkua81K89sclp/B1ckFxGP9YcvcHAyt22uQ56i4ePG1qdlsToZJPtiTHJeJXzLFYfYz8YTnxkAiduFhHeN/dLjE4y7TM6CS+/4vVpMRovFEUHhjjT7fr4/PUVRVFcUcqeRPE7j+L1LWrqO5Bz4Gaf+ddLC/cZV/C7EuLl4IxOoyEu0/R6bFxmBr5OxTs/ZwR2JDojjQM3rgDg4+SCq50DExu2ZM+1Szzz5y9sC7/A153709LP/+6d/cfYNHe43MwdaXfkjrQMfNzMTy/faeoT7YlNSePQbYXJs482R29QbbaG4hZ3Oxe0ipaknBST7Uk5qXjZmb90eqdRVfqSkJPMycT8wuRaZjQxWQmMrNIbF50TOkXLwIpd8XXwwtu+eH1aiqd7/vFLSDJ9byUkpePtWbz31qRhHYlLSDcpTG5nb6dl0rAObD8QREZmzgPHfC+8HG/lDtPzMy4rvfi5o/nN3HGzMPFxcsHV3p6JAS3Zc+0yz2xZw7YrF/m6a39alrNu7rinhZpxcXHo9Xr8/PxMtvv5+REcHGx2n+zsbLKzCyq/lJQUs+2sbfRjzenerA5jF/5CTl7+NGVd/7I83akpT8370SYxWdKQ6f14dEhbpnWaRW52rq3DsRw1HTW+DyguYN8axW0Gqj4i/9JIKTKxYUt6V6vLkC0/G695KigAbI8I5fvzxwA4nxBDs7IVGFanMX9HX7VZvPfqXnPHfyVvADzbqTk9Gtdm9FdrjLmjXoWyDG/fhEELf7JZXJbyZMXH6ODblBmnPyVXzZ+Z0KsG3gv6jhdrPs3q1vPRq3pOJoZwLOEc3DwvHxbP9G1B17a1mTx7NTm5hS9RabUa3nm5NwoKH363wwYRPpiJjVrSu1odhmxeVZA7lJu5IzyU78/eljv8KjCsbmP+jrJe7rD63R9z585lzpw5D9xPYlomeXoDZe74ZFHGzZm4lLuv1h3RpRljujVn/GfruBhZ8Km2aY0KeLs6s+Wd54zbdFoNUwd0ZFinpjzx1vcPHHdxJcelos/T4+XnYbLdq6wHiVFJd933yVd6M3R6P6Y/9jaXzxR8kkq4uZ+Xn6fx/2/9HHbqioUiLyZDIqqaV3gGQVsGDHdbTa7mr6cAyAsCXXUUlwmoOUcKZig0PqZ9aH0gN8ii4f+bxOwM8gwGfO74ZOHj5Exs5t3Pz3ENWjCxYSuGbVtNcGLBOBKzM8g16LmYbDoTE5ocT/OyFS0X/H+QpfIGQGL6zdzhekfucHUmLvXu15dHdWzGs50Cee6bX7lw47bcUbUC3i7ObJ851rhNp9Xwau8OPNO+Cd3nWveSzu1SctPRq3o875hB8LR3IzH37sVY/wqdedK/K2+e+ZwrGZEmvwtLu8oL/3yAs9YRnUZHSm4aHwe8wsW0iCJ6s46klPzjd+eshLenS6HZizs91TuQ4f1a8OI7awiLiCv0e61Ww7sv96acjzvPv/1Lic9SACRm3codpuenj6PLv+eOhs2ZGNCSYVt+ITjhttyRdTN3JJlelgtNiqd5ueJdjr1f93T5w8fHB61WS3S06W1V0dHRlCtXzuw+M2bMIDk52fi4evX+KqQ8vYGgq9G0qF2wUEpRoEXtSpy+ZP6WPYBRXQN5rkcrJi1ez/kI07g3Hgli0PvLGTJ3hfERk5TGDzuOMfHzX+8rzvuVl5vHheOXaNKlYGGOoig06dKQ84cvFLnf4Ff7MPzNJ5nZ4z0uHL9k8ruoyzHE30g0Wbjp7OZEnZY1OH8o5M6urCwXcs+h2N++cEwB+zaoufcyfawB5ebqc/1VVH1M/loLY5euYBdwj30+uFyDgTPxUSaLLBWg7SNVOBFzvcj9xjdowfMBbRi5fQ1n4k1vB8s1GDgdF0U1d9O7Y6q6e3M9zXaf3O/HveYOS+UNyM8d569H07JGwbSvokDLGv6cCi86d4x+NJDxXVoy4bv1nLtmGvcfJ4IYsGAFT37yo/ERnZzG0t3HGf/d+vuO9X7kqXpCU68S4Fmw9kpBIcCzFsEpV4rcb2DFLgyt9Dizzn5JaFrRr2+GPouU3DTKO/pSw60Sf8efKbKtNeTpDYRciqZZA9PcH9igEmcvRBa537A+zRk9sDVT319H8KXCtwLfKij8y3nx4jtr7rrewppyDQbOxEWZLLJUgLYVKnMiuujxjW/UguebtGHk1jWciTOTO2KjqOZxR+7w8OJ6qnVzxz3NVNjb29OsWTN27txJv379ADAYDOzcuZMpU6aY3cfBwQEHB4cHDhRgxc7jvDPicc5HRHP2ShTDOjfFycGO3w6fA+CdEY8Tk5TGZ7/vB2DUY82Z1LM1M5ZtITIhmTI3bz/KyM4lMzuX5PQsktNNT6Q8vZ74lHTCYxItEvO9WPfJRl5bNpkLx8IIORJK/5d64ujiwLaluwB4bdkU4iITWDJzJZB/u+iIOUOYO2wRUVdijeskMtOyyLo5rvWLNvH0GwO5fjGKG5djGPX2EOIjEzmw4WiJj0/NWILiMR9yz0LuaRSXUaA45d8NAvm/00ejpn2cv4PL+Py2+gjAHhw6glPf/O+hMPb5A4rrJFT9FdBfQ3F9CfQxkLW9pIfHd+eO8nG7npyOj+JU7A3G1A/EWWfHmov5SXhB+55EZaQy//heACY0bMnUJu14cc8fXEtLNl4/Tc/NISMv/xLW12f+5vNH+/J39DUO3Qjn0YrV6OpfgyFbVpb4+B7EveYOS+YNgOV7T/DekO6cuxbD2atRDG/fBCd7OzYczc8d7w/tTkxyGgu35K/XGfNoIFO6t+a1lVu4nphinCHNyM4lMyeX5Iwsk0WbkJ874lLTuRJb8rljw/VdvFx7OBdTI7iQGk7fCo/iqHFgR/RhAKbWeob4nCR+uPIHAAMrdmV45Sf4MPgHorPi8bTLv+MhS59NliH/03pbn8ak5KYRk51IFefyjKs+kMPxp/knyfylbmtatfEYb07uQfClaM6H3mDIE81wdLBj4+6zAPzf5B7EJqTx1c/7ABjetwVjB7dh9qebuBGTjLdH/vHLzMrP/Vqthven9qFW1bK8+sF6NBrF2CYlLYs8vaFEx/fd2WN83OEJTscVkTs6PkFUehrzj93MHY1aMLVZO17ctZFraSnmc8fpI3zeuQ9/R13l0I0IHq1Yla6VajBk089WHcs9X/6YOnUqI0eOJDAwkBYtWrBw4ULS09MZPXq0NeIz8eeJC3i5OTOxVxt83JwJuR7LpMW/knBzCvMRLzdUVTW2H9y+EfZ2Oj5+rrdJP19tOsRXm627OOx+7PnlIJ6+7oycMwSvcp6EnbzCzB7vkRSTv3izbCUfVEPB+HpN6Ia9gx2z1k4z6Wf5nF9YMWcNAKvn/4ajiyMvfT0eV09nzu4PZkaP92yz7iJrM6rGG8XtxZtffhWEmvgsGG5O0WnLk7/uOZ+iOIP7bNCWAzUL8i6hJk+DrM0FfaZ/A4oTivu7N7/86hhq4hig5KcxN14OpoyjM1ObtMPXyYXzCTGM+PMX4m7ewlXexR3Dbefn8NpNcNDq+Kqz6b3xn/yzn4Un8/+4bYu4yBuHtjGpUSvmtOxCWHICE3at59hdZj/+q2yZO7aeuoCXixNTurfGx82Z4MhYJny33rh48xFPN5NjM6R1I+x1OhaOMM0dX/x5iC+2H7Z6vPdqX9wJPOxcGV65J172blxKu85b574gKTf/S458Hbww3PbeeuKRdthp7JhZb6xJPyvDN7MyYgsA3vYejK02AE87NxJzUvgr5girIraW3KBus/NQCJ7uzjw3uC3ens5cvBLL1PfXkpicf/z8fEzfW/0fC8DeTsf7r/Q16ef7NQf5fs1BfL1dad88/06I5R+ONGkzefZq/jlfsuuVNl4KpoyjE1ObtsPX2YXz8TGM2LrGuHizvOsduaPuzdzRtZ9JP5+cOMDCEzdzR/hF3jjwJ5MCWjGn9c3csWMDx6KtmzsU9fa/wsX0+eef8+GHHxIVFUXjxo359NNPadmyZbH2TUlJwcPDgwZj30Nr73jPAT8MfL/87xUslrQt8qStQ7CqqlvG/nujh5QhM4trk2eTnJyMu3vJruKH+88dt/JGneffR+tQOvNGlT6X/r3RQyzhs8r/3ughdr1byc5ulCRDZhbXXphVrLxxXws1p0yZUuTlDiGEKIrkDiFKN/kHxYQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwCCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQmerJ06vABpHWz27dWW90cbWIVhV1d9a2DoEq9Kml95aW8my2VveIpQ2SSjODrYOwyqyO0bZOgSr2h+51dYhWFWblyfYOgSrycvVcq2YbUtv9hRCCCFEiZKiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwCCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAidrQO4V8ObBjC2ZSC+Li4ExcTy9vZdnL4RZbbtkICG9GtQl1q+PgCcjYrm4z0HTNp/0LM7AxvWN9lv76UrjPnlV+sN4i6ebhHAs+2a4ePqQnBULO9u2sWZ69Fm2w5q1oC+jetR068MAOciY/hk+/4i28/u3YWhLRrx/ubdLD/0j9XGcDcj6jdhXEBzfJ1cCIqPYdaBnZyKNX/8htZpxMBa9antnX/8zsRGM//I3kLta3h683rLjrR8xB+dRuFiYjwTtv9GZFqq1cdzp2cCGvNc4M3zMzaW2bv+4nRUEednw4YMqFuPWj43z8/oaD48sN+k/fzu3XmyfgOT/fZcuczoX21zfj7MhlZpwagabfFxcCUkJZq5ZzZxNum62bYDKzWjt39jarqVBeB8ciSLgnaYtD/T522z+358bhvLwg5YfgD/os+k7gya1gfvcp6EnQpn8QtLCDkaarZt5XoVGTlnCDWbVaNclbJ88fJS1i/abNJGo9HwzOxBdBnWAe9ynsRHJvDnD7v56d11JTGcwpyHobiMBY0v5Aajpr4NuafNt3XohuI6AbSVAR3ow1HTv4es34xNFNfnwbEnaB4BciH3LGraJ5B7qkSGc6eBjzVmeO9AvD1cCI2I5eNlf3E+zHzu6Nu5IT3a16NaxfzcEXI5mi9X7ze212o1TBjcltaNq1KhrCdpmdkcPRPOF6v2EZeYbtVx3PNMxd69e+nduzfly5dHURQ2bNhghbDMe6JOLWZ27shn+w/Td+mPBMfEsnTIALydncy2b1GpIhvPhzB85RoGLf+ZGympLBsyAD9XV5N2e8Iu0+qzr4yPl37bVBLDKaRHg1q83qMDi3cdZsCXPxESFcd3Iwfg7VLE+KpWZNOZYEYuWcvQb1YRlZzK9yMHUNbNpVDbrnWrE+BfjuiUNGsPo0i9qtfmzdaPsuj4QXqtW05QQiwreg6ijKOz2faty/vze2gQQ/9YTf8NPxGZnsKKnoPwcy44fpXcPVnb92nCkhIY+scquq/9gU9PHCI7T19SwzLqWas2Mzt25NPDh+j94wqCYmP5YcBAyjiZP36tKvrzR0gwT6/5hYE//8yN1FSWDxhY6PzcffkyLb760vh4cZNtzs8HYcu8AdC9fANerf84X4XsZvCer7iQHMXXrUbgbV/4vQLQ3KcKW66fZszBpQzf/y1Rmcl83XoEZR3djG0e3Tbf5PF//6zHoBrYceN8SQ3LqOPgNoz/eCQ/vr2Gic2mc+l0OHO3voGnr7vZ9g7ODty4HMP3M34i/kai2TZDpvel94RufP789zxb7yW+e/0nBr/al37P97DmUMxzfALFbSZq2ueocf0gLwjFawlovM23V5NQ075EjR+MGt8bNXMdisc8sG9X0CTvCmrK26jxvVAThoL+OorXUlCK6NOKuraqzYvPdOS7dYcYOXMFF8NjWfj6QLzczeeOpnX92X4wmMnv/sJzs34mOj6VRTMG4uuVnzsc7XXUrurH0vWHGTlzBa8v+J3K5b35cFo/q4/lnouK9PR0AgICWLx4sTXiuasxLZqx+tRZ1p05R2h8Av+3dQeZuXkMatTAbPtX/tjCT/+cIigmlksJiczcsh2NotC6ir9Juxy9nrj0DOMjJTu7JIZTyKg2TVlz7Cy//nOesNgEZv2xg6zcPAY2NT++V9du5ecjpwmOiuVyXCJvbrg5vuqVTNqVdXPhzZ6deHXtVvL0Jf/H9paxDQNZFXSaNSFnuZgUz8y9f5KZl8vgOubH9+Jfm1hx/iTn42MIS0pg+p5taBSFthUqG9u82rwduyIuMffvPZyLjyEiJYkd4WHEZ2WU1LCMnm3WjNVnz7D23DlCExJ4c8d2MvNyGdSgodn2L2/ZzI+nThEUG8ulxARe3/4niqLQxt/0+OXo9cRlZBgftjo/H4Qt8wbAiOptWBdxnA1X/+FSWixvn/6DTH0u/Ss1Ndv+9RPrWH3lKCEpUVxOi2PWyd/QoNDSp5qxTXx2msmjU7k6HIm7wrUM83+krWngy73Y8t1Oti3bTUTQNRZN+IbsjBy6j+lstv2FY2F8+9oKdq8+SG52rtk29VrX5uDvxziy+QTR4bHsW3eY43+eonbzGtYcilmK8xjIWA2Z60AfipryFqiZ4PSk+R1yjkD2dtCHgT4CMn6AvBAU+8CCNll/QM5B0F+FvFDU1LkoGjewq10yg7rNUz2b8dtfZ9i05xxXrifwwffbycrJpdej5nPHrMWbWbf9FBfDYwmPTOD9b/5EoygENsjPHemZObzw/lp2Hr5AxI1EzoXe4KOlO6lbrRx+ZdzM9mkp91xU9OjRg3fffZf+/ftbI54i2Wk0NCjnx4Er4cZtKnDwSjhNKjxSrD6c7HToNFqSM7NMtresVJG/n5/An8+NYk63Lng6Oloy9GKx02qoX96Pg5cijNtUFQ6FRdDY/x7Gp9WSnFEwPkWB+U8+zvf7jxMaE2/xuIvLTqOhoW859l83PX77r4XT1K98sfpw0umw02hIys4EQAE6V6rO5eRElj/xJMdHTGJDv2F0q1LySc9Oo6GBnx8Hwm87fsCB8AiaPFLM46fTYafVkJxlen62qliRIxMmsmPUaN7pYpvz80HZKm8A6BQt9Twe4XBsmHGbisrhuDACvCoWqw9HrV1+7sjNNPv7Mg4utPerxfqI4xaJ+V7o7HTUalaNEzsKLgWoqsqJHaep16rWffd7/lAITTo3oELN/PO3WqPKNGhXh6NbS/rSqR3Y1UfNOXjbNhVyDqLYNSleF/atQVsVNedo0c/hNATVkAK5wQ8a8D3RaTXUrurH0bOmuf/o2Qga1ixe7nB00KHVaUhJyyqyjauzAwaDSmqGdT+UPDRrKrycndBpNMSnm34CjUvPoFqZ4k1XvfZoe2LS0jhwpeDg7b10hT9DLnI1OYVKnh5M69iO7wcPYNCKnzGoqkXHcDdezk7otBri0+4YX1oGVX28itXHK93aE5OaZlKYPNe+OXqDyorDtllDcYuXY/7xi8u8Y3yZGVT3LN7xm9GyI9Hp6Ry4WZj4OLngam/PxMYt+Ojofub9vZeO/lX4uls/hv6xir9vXLP4OIri5XRzfBmm1yvjMjKo7l288U1v34HotHT2RxQUXnuvXGHbxVCupSRTycOTae3asXTAAAb+XLLn58PMy94ZnUZLfLbpsYnPTqeqq2+x+ni5Xjdis1I5HHvJ7O/7+DchIy+bHTeCHjjee+Xh44ZWpyUxOtlke2JMMv51Ktx3v6vmbcDZ3ZklQQsx6A1otBqWvvkzf63c/6Ah3xuNF4qiQzXEmW7Xx4N99aL3U1xRfPeDYg8YUFNmQ84da10cOqF4fAKKExhiUBNGgVqyM02e7vm5PyHZ9PxMTM6gSvni5Y7JT3cgLjGdo2fDzf7e3k7L5Kc6sP1gMBmZOQ8c891YvajIzs4m+7bp2pSUFGs/pVnjWzWnZ906DFv5Czm3XQLYFBRi/P8LsXGExMSxa+KztKxUkUPhV20R6n15rn1znmhYmxFL1pBzcz1B/fJleaZVEwZ++ZONo3twExu3oHf1Ogz5YzXZN4+fouT/bvuVUL4/k/8J8Xx8DM38KjCsXuMSLSoe1ITmLehVpzZP/2J6fm4MKTg/Q+LiCI6LZc+zY2lV0Z+DVyPMdVUq/FfyBsCzNdrTo0IDxhxcSo4hz2yb/v5N2HTtdJG/fxh1HNyazk+3Y+6wRVw5d40ajasw8ZNRxEcmsn35HluH9+/UdNT4PqC4gH1rFLcZqPqI/Esjt+Qczm+j8UZxGoziuQg14UkwJNgu7nv0TJ8WdG1dm8nv/EJObuHL21qthvde7I2iwAdLdlg9HqvfUjp37lw8PDyMD39//3/fyYzEjEzyDAbKuJgu6vNxcSYu/e6rWZ9t0YzxrZozavU6QmLj7tr2anIyCRkZVPbyvK8471diRiZ5egNlXO8Yn6szcWl3Xx8wpm0znmsfyNgffuVCdMH4mlWuQBkXZ/56ZSxnZ7/I2dkvUsHLg+mPd2Dn1DFWGUdRErPyj5+P0x3jc3ImNvPux29co+ZMbNyS4ZvWEJwQa9Jnrl7PxUTTyzqhSfFUcDW/QM1aEjNvjs/ZdOGfj7Mzsf9yfo5tFsiE5s0ZuW4dwXH/fn7GZ2RQ2dPzQUP+T7NU3gBIzMkgz6CnjIPpsSnj4EJ81t3vEBpZvS1jarZj3KHlXEgxf1dVU+/KVHXzZZ0NLn0AJMelos/T4+XnYbLdq6wHiVFJ993vc/OfYfUHG9i9+iBXzkaw48e9rFu4kaGvl/AlLEMiqpoHGh/T7doyYIg1vw8Aav56irwgyFgCWVtRXCbc0SQzv03uSdSUmYAenAZZegR3lZSSn/u9PUzPTy8PZ+KT7p47nu4ZyIg+zXlx7jpCIwrnjvyCohflfNx4/v21Vp+lgBIoKmbMmEFycrLxcfXq/X36zzUYOBsVTZsqBYvYFKBN5Ur8c/1Gkfs91zKQKW1aMeaX9ZyNMp8UblfOzRVPJydi06x7282dcvUGzkVG07paQfJUFGhVzZ+TV4se37PtApn4aEueW76es5Gm4/v9ZBB9F6+g/xc/Gh/RKWl8v/84Y5evt9pYzMk1GDgTG2WyyFIB2laozInoyCL3Gx/Qguebtmbk5rWciTMdX67BwOnYKKrdcfmkqoc311NNp4KtLddg4Gx0NG0q3XF+VqrEPzeKPn7jApvzfKtWjFr/K2eii3F+urri5eREzL8UKg87S+UNgDxVz/nkGyaLLBUUWvlU41Ri0bNZo2u0Y3ytjkw8vILzyUWfowMqNeVc0vUiiw5ry8vN48LxSzTpUrCoT1EUmnRpyPnDF+67X8eb1+BvZ9Ab0GiU++7z/uRC7jkU+9a3bVPAvg1q7r1c1tXcvBRy9zbKv7axrDy9gZDL0TRvcFvuUKB5/UqcuVh07hjeuzljBrTipXm/Enyp8Ll3q6DwL+fF8++tvet6C0uy+uUPBwcHHBwcLNLXkiPH+bDX45y5Ec3pG1GMCmyKk70da0+fA+DDXo8TnZrGR3vyr/mNa9mcl9q35uU/tnAtORmfm7McGTm5ZOTm4mxnx/PtWrMt5CKx6elU8vRgeqcOhCcmse+y+WtT1rTs4AnmDejO2esxnL4excjWTXCyt+PXE/njmzewOzEpaSzYnn9dcGz7QF7o3Jppa7ZwPSkFH9fbxpeTS1JmFkl3LErN0+uJS0vnclzJr1D/7swxPn70CU7HRnEq5gZjGgbibGfHmpCzACzo9ARR6anMP7IPgAkBLZjavC0v7tzEtdQUfJ3yK/n03Bwy8vJXrH996iifd+3N3zeucSgygkf9q9K1cnWG/LGqxMf3/fHjfPT445yJjuJUVBSjmzbF2c6Otefyx/fR448TnZbGh/vzz8/xzZvzUus2vLxlc/756Xzz+OUWnJ8vtG7N1ov552dlD0+md+hAeFIi+8KvlPj4SpIl8wbA8rCDvNekP+eSIzmTeI1nqrXGSWvPhqsnAHivyQBislJYFJQ/PTymRjsm1+7M9BNruZ6RRBmH/Fv1MvJyyNQXfNpz0TnwWPn6fHRuq8VivR/rPtnIa8smc+FYGCFHQun/Uk8cXRzYtnQXAK8tm0JcZAJLZq4E8hd3Vq6Xv0jVzl6HT4UyVA+oQmZaFpE3v+vg8B/HeXrmAGIi4gg/d5UaTaoy8OXebFv6V4mPT81YguIxH3LPQu5pFJdR+esgMvO/M0PxmA/6aNS0j/N3cBmf31YfAdiDQ0dw6ouaMiv/94oTistE1Oy/QB+Tv27DeTho/VCztpT4+H7edJz/m/g4QZeiOB8axZAeTXF0sGPTnvzc8dbEx4lNTOPLVfm545nezXluUBtmfb6ZG7HJeHvk547MrFwys3PRajXMfak3tav68cr89Wg0irFNSloWeXqD1cZyz0VFWloaoaEFX6hy+fJlTp48ibe3N5UqVbrLng9uc/AFyjg781L7Nvi6OHM+JpYxq38lPiP/8kB5dzeTxWtPN22EvU7H4v69Tfr5dP8hPt1/CL2qUsfXhwEN6uHm6EBMWhr7L4fzyd6DJte1S8qWsxfwdnHi+S6t8XV1JuhGLM8tX29cnFreww31tk8OTzXPH9+nT5mO7/O/DvH5rsMlGntxbAwLoYyjM1MD2+Lr7ML5uBhGbF5rXLxZ3tX0+A2v3xgHrY6vuvU16eeTYwdYeDx/Jfi2Kxd5Y9+fTGrSijltOxOWlMiEP3/jWJT5LzWypk0XQvB2duLlNm3xcXYmKDaWUb+uI+7W+enmbjK+YY0CcNDp+KJ3H5N+Fh06yKJDN89PH18G1KuPu0P++bkvPJxPDh6wyfn5IGyZNwC2RZ7F296ZybU74+PgSnBKFBMOrzAu3nzEyQP1tmMzuEpz7LU6Pmk+1KSfL0J28WXILuPPPSo0QAG2XD9j9THczZ5fDuLp687IOUPwKudJ2MkrzOzxHkkx+TN2ZSv5mOSOMuW9+OqfD40/D57Wh8HT+nBq9zmmdZ4NwOcvfM+od4bywuKxeJb1ID4ygU3fbOfHt9eW5NDyZW1G1XijuL1488uvglATnwXDzUuf2vLk32+VT1GcwX02aMuBmgV5l1CTp0HWzS/4UvWgq47i1D//uy4MiZB7BjX+Kcgz/4Vh1rTjcAie7k4892Rbyng6czE8lpfnrSMhOT93lPNxNzk/BzwWgL2djrkvm+aO79Ye5Lt1hyjr5UqHwPy74H78YIRJm0lvr+ZEkPXWmymqem9LyHfv3k2nTp0KbR85ciTLli371/1TUlLw8PCg6qz30DyEt8YVhy69pKcHS1ZmxdKzGM0cbXrp/fZ6Q1YW4W+8SXJyMu7uJbfuxFJ5o+7Pr6F1ttwMxn9JuX4lf+dISdoWedLWIVhVm5cn/Hujh1RebhbH1xUvb9zzTMWjjz7KPdYhQoj/cZI3hPjfUHo/kgkhhBCiRElRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwCJ2tntjlOmjtbfXs1uX75UFbh2BV2yJP2joEq6q6ZaytQ7AaQ2aerUN4IIZDnigOjrYOwyrsdj9i6xCsqu2LLW0dglXdeExv6xCsxpCph3XFayszFUIIIYSwCCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi9DZOoB7NaRDACO7BlLG3YUL12P54JddnA2PMtt2QJuG9GpZlxrlfQA4HxHN578fKLL9G0O7MKh9AB+u3cVPu/6x2hjups+k7gya1gfvcp6EnQpn8QtLCDkaarZtj7FdeOyZjlRp4A/AxeOXWPLGz4Xaj5wzhB5ju+Dq6cK5A8F8Oulbroeafw2sznkYistY0PhCbjBq6tuQe9p8W4duKK4TQFsZ0IE+HDX9e8j6zaSZ4voiOA0GjTvkHEdNmQX6cOuPxYwRdZowrkFLfJ1cCEqMYdbhHZyKu2G27dBaAQysXp/aXr4AnImPYv7xvYXa1/Aow+uBHWlZrhI6ReFiUjwTdq0nMj3V6uMpTZ5qHcDojs3wcXMh5EYs7/+2izNXo822fbJFA/o0q0cNvzIAnL8ew6Kt+4ts/9aALgxp1Yh5v+9mxX7b5I5ej7RnoH9nvOzduZx2nS/D1nIhNcJs2+7lWtPFrwWVnR8BIDTtKj9c+cOkvaPGntFV+9DapxFuOmeisxL4PXIPm28cKJHx3Glgt8YM690cb08XQsNjWbB0J+fDzOexPp0b0qNDfar55+f+kMvRfPXzPmN7rVbD+CHtaNOkKuXLepKWkc2xs+F8sXIvcYnpJTam242o24RxjVrk546EGGYd2sGpWPPjG1q7EQNr3pY74qKYf2xvofY1PL15vfmjtHzEvyB37Nhg1dxxTzMVc+fOpXnz5ri5uVG2bFn69etHSEiItWIrpFvTWrwyoCNfbz7MU/N+5MK1WL6YMgAvVyez7QNrVWTrsRCeW7SGER/9THRiKl9OGUBZD9dCbTsF1KBR1UeISUqz9jCK1HFwG8Z/PJIf317DxGbTuXQ6nLlb38DT191s+4CO9dm1aj+vdp7Di23eIPZqPPO2vUmZ8t7GNkNe60u/53uwaOI3PN9qBlnp2czd+iZ2DnYlNawCjk+guM1ETfscNa4f5AWheC0Bjbf59moSatqXqPGDUeN7o2auQ/GYB/btCtq4jAPnEagpb6HGPwlqJorXUsC+JEZkolfVOrzZojOLTh6g1+/LCEqIYUW3wZRxdDbbvnU5f36/HMTQrT/Tf9MKItNTWdFtMH7OBednJTdP1j4xjLDkBIZuWUn335by6amDZOv1JTUsi7B17ng8oBav9e7AFzsOM2jRT4TciOPrZwfg7WI+dzSvXpHNJ4MZ8/Vahi1eRVRSKt+MHUBZd5dCbbvUr05ApXJEJ9sud3TwbcJz1fuzMnwrz5/4kEvp13mnwSQ87ArnOoBGnjXZE3OcGac/45WTC4jLTuTdhpMoY+9hbPNc9f40867Lh8HLGX/sfTZc383EGk/S0rtBSQ3LqEvr2rww4lG+X3eIUa+v4GJ4DJ/MfBIvd/Pvrab1/dl+MJgpb69m3P+tJDo+lYVvPImvV/7r4Wivo3bVsixdd5hRry9nxoLfqPSIN/Nf7V+SwzLqVa0Ob7bqxKITB+i14QeCEmJZ8fhdcscjlfg9LIihm1bR//cf83PH42ZyR69hhCXHM3TTz3T/dRmf/nPI6rnjnoqKPXv2MHnyZA4fPsz27dvJzc2lW7dupKeXTGX3TJdm/HrwLL8dPselqATeXbWDrJw8+rU2f5LPXLaFX/adIuRaLFeiE5nz03YURaFFbX+TdmU9XHl9UCdmLttCng2T9cCXe7Hlu51sW7abiKBrLJrwDdkZOXQf09ls+3nPfMofX/5J2KkrXA2JZMFzX6FoFJp0KXg9+r/Yk5/eW8eh349x+UwEH4z8nDLlvWjbr3lJDctIcR4DGashcx3oQ1FT3gI1E5yeNL9DzhHI3g76MNBHQMYPkBeCYh94W58jUdO+gOydkBeCmvwqaMuC42MlNKoCY+s3Z9WFU6wJPcPF5HhmHtxGZl4ug2s2NNv+xb0bWRH8D+cTYghLTmD6gS1oFIW2j1Q2tnm1aQd2XQtj7rHdnEuIISI1iR1XQ4nPyiipYVmErXPHyPZNWfv3WTYcO09YTAJzft1BVm4eA5qbzx3Tf97KqkOnCb4Ry+XYRN5aux2NotCqRiWTdmXdXZjZtxOv/bzVprmjf4VObL1xkO3Rf3M1I4rPL/5CtiGHbuVamW3/YfByNt3Yz6X061zLjGHRhZ/RoCHAs5axTV33quyMPsKZ5FBishPYGnWQS2mR1HavbLZPa3qqZyC/7zzDpt1nuXI9nvnfbSc7J5dencwfv9mfbebXP09yMTyW8MgE5n61DY2iENgw//ilZ+bw4ntr2Xk4hIgbiZy7eIOPl+6kbvVy+JVxK8mhATC2QSCrgk+z5uJZLibFM3P/zdxRq4jcsXsjK4JOFuSOfVvzc0f523JHYHt2Xb3E3CN7OBd/M3dEWD933FNRsXXrVkaNGkX9+vUJCAhg2bJlREREcPz4cWvFZ6TTaqjr78ffwQXT2qoKfweH06jaI8Xqw9Feh06rJTkjy7hNUeDdkY/zw45jhN2It3jcxaWz01GrWTVO7Ci4FKCqKid2nKZeq1p32bOAg7M9OjsdqQn5n5jKVS1LmUe8+GfHGWObjJQMgv8OpV7r2pYdwL+yA7v6qDkHb9umQs5BFLsmxevCvjVoq6LmHM3/WeuPoi0Lt/eppkHuqeL3aSF2Gg0Ny5Rjf+Rt5yew/8YVmpatUKw+nLR22Gk0JGXnn58K0Nm/GpdTElnebTDHh05hQ69n6FapphVGYF22zB12Wg31KvhxKLRgal9V4fDFCAIq32PuyDTNHfOGPs7SPccJi7Zh7lC01HDz52RSwcyPisrJpBDquFUtVh8OWnu0ioa0vII/OEEpl2lZpoFx9qKRR00qOPlyIjHYsgP4FzqthtrV/Dh6xjT3Hz0TQYOa5YvVh6ODDp1OQ0paVpFtXJ3tMRhUUjOyHzjme2Gn0dDQpxz7I68Yt6nA/uvhNPUr3vicdOZyR3UuJyew/PFBHB82mQ19htOtcg3LD+AOD7RQMzk5GQBv7yKmry3Iy9UJnVZDfKpplRWfmoGPmSlJc17q157Y5DT+Di5ILqMfa47eYGDlbttcB73Fw8cNrU5LYnSyyfbEmGS8ynkWq4+xHwwnPjKBEzeLCO+b+yVGJ5n2GZ2El1/x+rQYjReKogNDnOl2fXz++oqiKK4oZU+i+J1H8foWNfUdyLl5TVeTf720cJ9xBb8rIV4Ozug0GuIyTT95x2Vm4OtUvPNzRmBHojPSOHDjCgA+Ti642jkwsWFL9ly7xDN//sK28At83bk/Lf38797Zf1xJ5g5PlyJyR1oGPm7mp5fv9EqP9sSkpHHoYkHuePbR5uQZVH48YNvc4W7nglbRkphjep08KScVb/vifeoeXbUPCTkp/JNYUJh8GbqOiIwoVrR6h9/bfcI7DSfyRegaziaHWTT+f+Ppnn/8EpJN31sJyemU8Szee2vSsI7EJqSbFCa3s7fTMunpDmw/GERGZs4Dx3wvvBxv5Q7T8zMuK734uaP5zdxxszDxcXLB1d6eiQEt2XPtMs9sWcO2Kxf5umt/Wpazbu6474WaBoOBl156ibZt29KgQdHX2LKzs8nOLqj8UlJS7vcpH8jox5rTvVkdxi78hZy8/GnKuv5lebpTU56a96NNYrKkIdP78eiQtkzrNIvc7Fxbh2M5ajpqfB9QXMC+NYrbDFR9RP6lkVJkYsOW9K5WlyFbfjZe81RQANgeEcr3548BcD4hhmZlKzCsTmP+jr5qs3gfRHFyx38lbwCMfbQ5PRrXZtRXa4y5o16FsjzTrglPLvrJZnFZyiD/rnT0bcr005+Rq+YZt/ep0IE6blWYffYbYrITaOBRnUk1BpGQk8zJpAs2jPjePNO3BY+1qc2kOavJyS18iUqr1fDuS71RFIX53+2wQYQPZmKjlvSuVochm1cV5A7lZu4ID+X7s7flDr8KDKvbmL+jrJc77ruomDx5MmfPnmX//v13bTd37lzmzJlzv09jlJiWSZ7eQJk7PlmUcXMmLuXu12VHdGnGmG7NGf/ZOi5GFnyqbVqjAt6uzmx55znjNp1Ww9QBHRnWqSlPvPX9A8ddXMlxqejz9Hj5eZhs9yrrQWJU0l33ffKV3gyd3o/pj73N5TMFn6QSbu7n5edp/P9bP4edumKhyIvJkIiq5hWeQdCWAUPsXXZU89dTAOQFga46issE1JwjBTMUGh/TPrQ+kBtk0fD/TWJ2BnkGAz53fLLwcXImNvPu5+e4Bi2Y2LAVw7atJjixYByJ2RnkGvRcTDadiQlNjqd52YqWC76EFSd3WCpvACSlF5E7XJ2JS7379eVRHZrxbKdAxn77KxeiCo5Ds6oV8HZxZseMscZtOq2GV3t14Jl2Teg2b4lFYi+OlNx09KoerztmJTzt3UjIufsq/wEVOzPIvytvnF7MlfRI43Z7jR0jq/Ti3fPfcTThPABX0iOp7lKRARW7lGhRkZSSf/y8PUzfW94eLsQn3f299XSvQJ7p24IX3l1DWERcod9rtRree6k35XzdmfL2LyU+SwGQmHUrd5ienz6OLv+eOxo2Z2JAS4Zt+YXghNtyR9bN3JFkelkuNCme5uWKdzn2ft3X5Y8pU6awceNGdu3aRcWKd09uM2bMIDk52fi4evX+KqQ8vYGgq9G0qF2wUEpRoEXtSpy+ZP6WPYBRXQN5rkcrJi1ez/kI09vBNh4JYtD7yxkyd4XxEZOUxg87jjHx81/vK877lZebx4Xjl2jSpWBhjqIoNOnSkPOHi34DD361D8PffJKZPd7jwvFLJr+LuhxD/I1Ek4Wbzm5O1GlZg/OHSm7lfb5cyD2HYt/6tm0K2LdBzb2X6WMNKDfv7NBfRdXH5K+1MHbpCnYB99jng8s1GDgTH2WyyFIB2j5ShRMx14vcb3yDFjwf0IaR29dwJt70drBcg4HTcVFUcze9RFDV3Zvrabb75P4gips7LJU3AHL1Bs5fj6ZVjYJpX0WBljX8ORVedO4Y0zGQCV1aMv779Zy7Zpo7fj8RRP9PVjBw4Y/GR3RyGkv3HGfc9+vvO9b7kafqCU29arLIUkGhsWdtglMvF7nfkxW78FSl7vzfma+4mGb6+moVLXYaHaqqmmzXY0Bz81NwScnTGwi5FG1cZAn5xy+wQSXOXowscr9hfZozemBrXp67juBLhW8FvlVQVHzEixfeWXPX9RbWlGswcCYuymSRpQK0rVCZE9FFj298oxY836QNI7eu4UycmdwRG0U1jztyh4cX11OtmzvuaaZCVVWef/551q9fz+7du6la9d8XATk4OODg4HDfAd5uxc7jvDPicc5HRHP2ShTDOjfFycGO3w6fA+CdEY8Tk5TGZ7/nfwIa9VhzJvVszYxlW4hMSKbMzduPMrJzyczOJTk9i+R00xMpT68nPiWd8JhEi8R8L9Z9spHXlk3mwrEwQo6E0v+lnji6OLBt6S4AXls2hbjIBJbMXAnk3y46Ys4Q5g5bRNSVWOM6icy0LLJujmv9ok08/cZArl+M4sblGEa9PYT4yEQObDha4uNTM5ageMyH3LOQexrFZRQoTvl3g0D+7/TRqGkf5+/gMj6/rT4CsAeHjuDUN/97KIx9/oDiOglVfwX011BcXwJ9DGRtL+nh8d25o3zcrien46M4FXuDMfUDcdbZseZi/hqXBe17EpWRyvzjewGY0LAlU5u048U9f3AtLdl4/TQ9N4eMvPxLWF+f+ZvPH+3L39HXOHQjnEcrVqOrfw2GbFlZ4uN7EPeaOyyZNwB+2HeC9wd359y1GM5cjeKZdk1wsrdj/bH83PH+kO7EJKexcGv+ep1nHw1kSrfWvLZyC5EJKfi43swdOblk5OSSnJFlsuAb8nNHXGo6V2JLPnesv76LqbWHczHtKhdSwulb8VEcNPZsj/obgFdqDyc+O5llV/4A4MmKXXmmyhPMD/6BmKx4vOzyZzky9dlkGXLI1GdxOukiY6r1JTs0l5jsBBp61KBL2eZ8e2lDiY/v503H+L9JPQgOi+Zc2A2GPtEMRwc7Nu4+C8Bbk3sQm5DGlz/vA2B4nxY8N7gNsz7dxI2YZLw98o9fZlZ+7tdqNbz/ch9qVy3LtPnr0WgUY5uUtCzy9IYSHd93Z4/xcYcnOB1XRO7o+ARR6WnMP3YzdzRqwdRm7Xhx10aupaWYzx2nj/B55z78HXWVQzcieLRiVbpWqsGQTT9bdSz3VFRMnjyZlStX8ttvv+Hm5kZUVH515OHhgZOT+fu9LenPExfwcnNmYq82+Lg5E3I9lkmLfyXh5hTmI15uJpX14PaNsLfT8fFzvU36+WrTIb7afMjq8d6rPb8cxNPXnZFzhuBVzpOwk1eY2eM9kmLyF7WVreSDaigYX68J3bB3sGPW2mkm/Syf8wsr5qwBYPX833B0ceSlr8fj6unM2f3BzOjxnm3WXWRtRtV4o7i9ePPLr4JQE58Fw80pOm158tc951MUZ3CfDdpyoGZB3iXU5GmQtbmgz/RvQHFCcX/35pdfHUNNHAOU/DTmxsvBlHF0ZmqTdvg6uXA+IYYRf/5C3M1buMq7uGO47fwcXrsJDlodX3U2vTf+k3/2s/Bk/h+3bREXeePQNiY1asWcll0IS05gwq71HLvL7Md/ka1zx9ZTF/B2cWJKt9b4uDkTHBnL+O/XE592M3d4muaOIa0aYa/TsXCEae5YvP0QX2w/bPV479Xe2H9wt3PlmcpP4GXvzqW0a7x19kuScvMvf/g6eJmcez3Lt8VOo+ONes+a9PNT+BZ+Ct8CwAdByxhVtTev1hmBm86ZmOxEll/ZxOYbd7/kbQ07D4Xg5e7M2MFtKePpzMUrsbw8dy2JyfnHz6+MO4bbcuOAxwKwt9Mx95W+Jv18t+Yg3689iK+3Kx2a598JsWL+SJM2k+as5p/zJbteaeOlYMo4OjG1aTt8nV04Hx/DiK1rjIs3y7vekTvq3swdXfuZ9PPJiQMsPHEzd4Rf5I0DfzIpoBVzWt/MHTs2cCzaurlDUe+c37pb4yKmvZYuXcqoUaOK1UdKSgoeHh40GPseWnvH4j71Q8X3y/9ewWJJ2yJP2joEq6q6Zey/N3pIGTKzuDZ5NsnJybi7m/9SNWt40NxxK2/UfuF9tA6lM29U7lX0pYrSIHFxpX9v9BCLfKxkZzdKkiEzi2svzCpW3rjnyx9CCHGvJHcI8b9B/kExIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCigohhBBCWIQUFUIIIYSwCCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRF6Er6CVVVBUCfk1XST11i8tRcW4dgVSmpBluHYFWGzNJ7bt4a26334cPCmDeyS++xyU3PsXUIVpWXW3qPHYAhs/TmxXvJG4pawtnl2rVr+Pv7l+RTCiHucPXqVSpWrGjrMIpN8oYQtlecvFHiRYXBYCAyMhI3NzcURbH686WkpODv78/Vq1dxd3e3+vOVNBnfw62kx6eqKqmpqZQvXx6N5uG5+il5w7JkfA+3/3LeKPHLHxqNxiafkNzd3UvlyXWLjO/hVpLj8/DwKJHnsSTJG9Yh43u4/RfzxsPzUUUIIYQQ/2lSVAghhBDCIkp9UeHg4MCsWbNwcHCwdShWIeN7uJX28T2sSvtxkfE93P7L4yvxhZpCCCGEKJ1K/UyFEEIIIUqGFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIiSnVRsXjxYqpUqYKjoyMtW7bkyJEjtg7JYvbu3Uvv3r0pX748iqKwYcMGW4dkMXPnzqV58+a4ublRtmxZ+vXrR0hIiK3Dspgvv/ySRo0aGb+4pnXr1mzZssXWYYnblNbcUZrzBkju+C8otUXF6tWrmTp1KrNmzeLEiRMEBATQvXt3YmJibB2aRaSnpxMQEMDixYttHYrF7dmzh8mTJ3P48GG2b99Obm4u3bp1Iz093dahWUTFihWZN28ex48f59ixY3Tu3Jm+ffty7tw5W4cmKN25ozTnDZDc8Z+gllItWrRQJ0+ebPxZr9er5cuXV+fOnWvDqKwDUNevX2/rMKwmJiZGBdQ9e/bYOhSr8fLyUr/77jtbhyHU/53cUdrzhqpK7rCFUjlTkZOTw/Hjx+natatxm0ajoWvXrhw6dMiGkYn7kZycDIC3t7eNI7E8vV7PqlWrSE9Pp3Xr1rYO53+e5I7SRXJHySvxf1CsJMTFxaHX6/Hz8zPZ7ufnR3BwsI2iEvfDYDDw0ksv0bZtWxo0aGDrcCzmzJkztG7dmqysLFxdXVm/fj316tWzdVj/8yR3lB6SO2yjVBYVovSYPHkyZ8+eZf/+/bYOxaJq167NyZMnSU5OZu3atYwcOZI9e/b8p5KDEA8zyR22USqLCh8fH7RaLdHR0Sbbo6OjKVeunI2iEvdqypQpbNy4kb1799rkn722Jnt7e2rUqAFAs2bNOHr0KIsWLeLrr7+2cWT/2yR3lA6SO2ynVK6psLe3p1mzZuzcudO4zWAwsHPnzv/UtSdhnqqqTJkyhfXr1/PXX39RtWpVW4dkdQaDgezsbFuH8T9PcsfDTXKH7ZXKmQqAqVOnMnLkSAIDA2nRogULFy4kPT2d0aNH2zo0i0hLSyM0NNT48+XLlzl58iTe3t5UqlTJhpE9uMmTJ7Ny5Up+++033NzciIqKAsDDwwMnJycbR/fgZsyYQY8ePahUqRKpqamsXLmS3bt3s23bNluHJijduaM05w2Q3PGfYOvbT6zps88+UytVqqTa29urLVq0UA8fPmzrkCxm165dKlDoMXLkSFuH9sDMjQtQly5dauvQLGLMmDFq5cqVVXt7e9XX11ft0qWL+ueff9o6LHGb0po7SnPeUFXJHf8F8k+fCyGEEMIiSuWaCiGEEEKUPCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBZR6ouK7OxsZs+e/Z/6xjFLkvE93Er7+B5Wpf24yPgebv/l8ZX676lISUnBw8OD5ORk3N3dbR2Oxcn4Hm6lfXwPq9J+XGR8D7f/8vhK/UyFEEIIIUqGFBVCCCGEsIgS/wfFDAYDkZGRuLm5oSiK1Z8vJSXF5L+ljYzv4VbS41NVldTUVMqXL49G8/B8ppC8YVkyvofbfzlvlPiaimvXruHv71+STymEuMPVq1epWLGircMoNskbQthecfJGic9UuLm5AVDllbfQODiW9NOXiIMjv7R1CFYVuOk5W4dgdTVfO2XrEKwiT81ln/534/vwYXEr3vATVXB3fXhmWO5F/1oNbR2CVWV3a2rrEKzO8YUbtg7BKvIyctg3eEmx8kaJFxW3pi41Do5oHUtnUeHuVjqT3i0ap9J53G6nU+xsHYJVlcQlBEu6Fa+7q6bUvr9K+zmnt/sfyBsuDrYOwaqKkzdK57tTCCGEECVOigohhBBCWIQUFUIIIYSwCCkqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhETpbB3CvhgUG8GzrZvi6uhAcHcs7W3dxOjLabNvBTRrQr1E9avqWAeDcjRgW7Npv0n5en24MCKhvst/e0CuM/Xm99QZxF1rnZ9C5jEfR+qLmBpGTMgs195T5tk5D0ToPQKOrDYAh9wy5qR8Waq/oqmPn9joa+5aADjXvIjmJE1ENkdYeTiEj6jdhXOPm+Dq5EBQfw6wDOzkVE2W27dC6jRhYqz61vX0AOBMbzfwje03af9SpB4NqNzDZb3fEZUZuXmu9QdxF7wmPMWhqb7zLeXDpdASLX1pGyLEws217jOlM1+HtqVK/IgAXT1xm6f+tLrL9C58/S69xXfnyleWs/2yL1cZQajkPQ3EZCxpfyA1GTX0bck+bb+vQDcV1AmgrAzrQh6Omfw9Zv5m2cX4K7OqjaLwwxPWBvKASGYo5fSZ1Z9C0PniX8yTsVDiLX1hCyNFQs20r16vIyDlDqNmsGuWqlOWLl5eyftFmkzZOro6Memcobfu1wLOsB6H/XOaLl5ZyoYjz09r692zC0AEt8PZyIexyDIu+3kHQBfO5o1f3RnTvXJ9qlX0BCAmN4tvle43ttVoNzz3TnlaB1XiknAfp6TkcO3WFr5ftJT4hrcTGdLve5dsxqFJnvO3duJQWyeKL6whJjTDbtscjrejq15wqLo8AcDHtKksvbTJp76i159lqvWnj0xB3nTNRWQlsuL6XTZEHrTqO+5qpWLx4MVWqVMHR0ZGWLVty5MgRS8dl1hP1ajHjsQ58vvcw/b79ieDoOL5/egDezk5m27eoXJGNZ4MZsWItQ5au4kZKKkuGDcDPzcWk3d7Qy7RZ8LXxMXX9ZrP9WZvWsRd27m+Sl7aI7LieGPLO4+C9HDRlzLbXOLRCn/k72fFPkR03AFV/AwfvFaDxM7ZRtJVwKLMWQ17YzXaPk5v2GSrZJTUso17Va/Nmm0dZdOwgvdYtJyg+lhU9B1HG0dls+9bl/fk9NIihv6+m//qfiExLYUXPQfi5uJq02x1xicAfvjA+nt/xR0kMp5COg1ox/sNn+PHddUxqOZNLp8N5f9PrePq6m20f0LEuu1cf5NVu7/JSh1nEXotn7uYZlCnvVaht276B1G1Zg7jrCdYehlXZKnfg+ASK20zUtM9R4/pBXhCK1xLQeJtvryahpn2JGj8YNb43auY6FI95YN+uoI3ihJpzHDX1wxIZwt10HNyG8R+P5Me31zCx2XQunQ5n7tY3ijz3HJwduHE5hu9n/ET8jUSzbaZ+O5GmXRvxwYjPGNfoFY5vP8X87W9RpnwRr5kVdW5fh8ljO7Hs5wOMffEHQi/H8tHbg/H0MJ87mjSsxM49Qbw4YxUTp/1ITGwqH709GJ8y+bnD0UFHzep+/LDqIGNfXM6b76+nUgVv5v7fgJIcllFH3yaMr9GPH69sZdKxj7iUdp33G03A087VbPsAzxrsjjnBq6cW89I/C4nNSmJuwETK2HsY20yo3o9A7zp8EPQjY4/OY/21PUypOZBWZeqb7dNS7rmoWL16NVOnTmXWrFmcOHGCgIAAunfvTkxMjDXiMzG6VVN++ecsv546T1hcAm9t2kFWbh5PNm5gtv20DVtZefw0QdGxXIpP5I2N29EoCq2rVjJpl6PXE5eeYXykZJX8H1wAnctY9Bmr0GeuQc0LJTf5DVAz0TkNNts+N+kl9Bk/ouadR9WHkZs8HVDQOrQt6NPtVfRZu8hLnYeadw5VH4EhewcY4ktoVAXGNgpkVdBp1oSc5WJiPDP3/klmXi6D65g/fi/u3MSKcyc5Hx9DWFIC0/dsQ6MotK1Q2aRdtl5PbGa68ZGSY5vjN/DFnmz5/i/+XL6HiKDrLJr8PdkZOXQf9ajZ9vNGLuaPr7dz6VQ4V0Mi+WT8NygahSadTV+PMuW9mPTJKOaNXExerr4ERmIdtswdivMYyFgNmetAH4qa8haomeD0pPkdco5A9nbQh4E+AjJ+gLwQFPvAgjZZv0H655Bj3U9+xTHw5V5s+W4n25btJiLoGosmfJN/7o3pbLb9hWNhfPvaCnavPkhudm6h39s72tN+YEu+nf4jZ/YFERkWxYo5a7geGkXvid2sPZxCBvcLZOO202zZcZbwq/F8vHgbWdm59Hysodn273y0kQ2bTxJ6OYaIawnM/2wrGo1Cs4D83JGekcMr//cLu/aHcPV6AudDbrDwqx3UqVmOsr5uJTk0AAb6P8qWG4f4M+oIERnRLLqwhmxDDt0faWm2/bygH/kj8gCX0q5zNSOGT0JWoaDQxKuWsU09j6rsiDrK6aRQorMS2HzjEJfSIqnjXtlsn5Zyz0XFggULeO655xg9ejT16tXjq6++wtnZmSVLllgjPiM7jYb6j/hx8HLB9I4KHLwcQeOKjxSrDyc7HTqNlqTMLJPtLSpX5NDU8WydNJLZPTrj6eRoydCLyQ7FrgH67AO3bVPRZx9AY9+0eF0oTqDYoRqSbm1A69AJVX8Ze+/lOJY9hkOZDWgcSj4p2Gk0NPQtx/5r4cZtKrD/WjhN/coXqw8nnQ47jYakrEyT7a3K+3N85CT+Gvos77Z/DE+Hkj9+OjstNZtW5Z+/zhq3qarKP3+dpW6rmsXqw8HZAZ2djtTbpl8VRWH60smsWbCR8PPXLB53SbJV7gA7sKuPavLHX4Wcgyh2TYrXhX1r0FZFzTlqlQgfhM5OR61m1Tixo+BSjqqqnNhxmnqtat1lz6JpdRq0Oi25WTkm23Myc2jQts4DxXuvdDoNtWqU49jJK8ZtqgrHT4ZTv07xcoeDgx06rYaU1Kwi27g4O2AwqKSlleyHEp2ipaZbRf5JvGDcpqLyT+IF6rpXKVYfDlp7dIqG1Lx047bzyZdpVaaBcfYiwLMGFZx8OZ4QbNH473RPaypycnI4fvw4M2bMMG7TaDR07dqVQ4cOmd0nOzub7OyCg5SSknJfgXo5O6HTaIhLyzDZHpeeQTWfwtPF5kzr0p6Y1DQOXiooTPaFXeHP4FCuJSVTycuTqZ3a8t1T/Rm8dBUGVb2vWO+LxgtF0YEhzmSzaohFo6terC7s3F5H1UdjuFWYaHxQNK7oXCaSm/YxuSnz0Dp0xN7rK3ISnsKQ87elR1EkL8ebxy/zjuOXmUF1z+JNp85o1ZHo9HQOXC8oTPZEXGbrpQtcTU2msrsnr7Vozw89n6T/+p9K9Pi5+7ij1WlJjE422Z4Yk4x/7eIlvrHvP018ZCIndhYUJkNe7YM+T8+Gz7daNN6Sdq+5w1J5I/+J8t9b6h3vLfTxYH+X95biiuK7HxR7wICaMhtyDhTd3kY8fNyKPvfqVLivPjPTsjh3MIRhbz5JRNB1EqOT6fRUW+q2rkVkqPl1DNbi4e6MTqshMck0dyQkpVOpYvFyx4RRHYlLSOP4bYXJ7ezttEwY3ZGde4PIyMwx28Za3O1c0CpaEnNSTbYn5qTi7+xXxF6mxlbrTXxOCiduK0wWX1zHS7WH8HObOeQZ9BhQWRiymjPJlywa/53uqaiIi4tDr9fj52c6UD8/P4KDzVc/c+fOZc6cOfcfoYWMa9OcnvVr88zyNeToC6aQN50rOAgXYuIJiY5j5/NjaFm5IoeuXLVFqPdF5zIRrVNvsuOHgnG9hAKAPns7+vTvAcjLO4/Gvila52ElWlQ8qImNW9C7eh2G/L6a7NuO3x9hBeddSEIcQfGx7B82jtbl/Tlw3fwip/+iIa/2oePg1rz62DvG6eiaTarSb8rjTGo508bRPbh7zR3/ibyhpqPG9wHFBexbo7jNQNVH5F8a+R/wwYjPmPb9JFZd/wZ9np6LJy6z6+f91GpWzdah3ZNhT7akS4c6vDBjFTlmLh9qtRrmvN4XBYWPF/9pgwgfzJBKXehYtgmvnvycXEOecXvfih2o416Ft858S3RWAg09qzOl5kDic5JNZkUszeq3lM6YMYPk5GTj4+rV+/tDnZiRSZ7BgI+r6cIcHxdnYu+YvbjTmFbNGNc2kDE//UpITNxd215NSiYhPYNK3p73Fed9MySiqnmg8THZrGh8UQ2xd91V5/IcOteJZCc8g5p3W4I2JKKquah5F02fKi8MRVu8T8+Wkph18/g53XH8nJyJzUgvYq984wKaM7FJS4ZvWkNwwt1fi6upycRnZlDZvXizV5aSEpeCPk+Pl5+HyXavsh4kRCfddd8nX+7JkFf7MOOJuVw+U1AINWhXB8+y7vwU9hlbMn5kS8aPlKviy7j5w1l+4VNrDOM/w1J5AyjyvYW2DNz1vaXmr6fIC4KMJZC1FcVlwv3HYSXJcalFnnuJUUn33e+NS9G80mkWvV2H83SlCTzfagY6Ox03Lll/DcztklMyyNMb8PI0zR3eni4kJN49dwzt35ynn2zJK/+3hktXCh/r/IKiD35l3Zn6f6tLfJYCICU3Hb2qx8vedC2Hl70bCTl3n6F70r8TQyp1Zcbpr7icfsO43V5jx+iqPfk6dAOH489xOf0Gv1/fz57Yf3jSv5NVxnHLPRUVPj4+aLVaoqNNb+GMjo6mXLlyZvdxcHDA3d3d5HE/cg0Gzt2IpnUVf+M2BWhd1Z+T124Uud/Y1oFMbt+SZ1eu5+wN87ee3s7PzRVPZydi0+5+slpeLmruWbQObW7bpqB1aIMh50SRe+lcxqNzfZ7shJGouWcK9WnIPY2iNf1kodFVRdVft1zoxZBrMHAmNspkkaUCtK1QmRPRRd/aOr5xC55v2pqRm9ZyJvbfj185F1e8HJ2IySjZ28LycvM/yTXuVLDIUlEUGneqT9Dhi0XuN+iV3gybOYCZveZx8YTptOSOn/Yxodl0JjZ/3fiIu57AmgV/MLPXXKuNxRruNXdYKm/ky4Xccyj2rW/bpoB9G9Tcf+6hH83NSyH/LXm5eVw4fokmXQoWLSqKQpMuDTl/+ME/kWZlZJMQlYSrpwuB3QM4+HvJrivJyzNwITTKuMgSQFGgaUBlzgUXnTueGtiCEUPb8OqsNYSYuWRzq6CoWN6Ll99Yfdf1FtaUp+q5mHqNxp4Fa68UFBp71SIo5UqR+w3y78ywyt2YeforLqaaFt06RYOdRoeK6SVgg6qiuTmDbS33dPnD3t6eZs2asXPnTvr16weAwWBg586dTJkyxRrxmVh6+AQf9O3O2RsxnI6MYmSLJjjZ2bHu1DkA5vftTnRqGh//lX/d87k2gbzYsTVT12/helIKPi75lW5GTi4Zubk429kxpUMrtgVfJC4tg0peHrzatT3hCUnsCwsvMg5ryUv/DjvPjzHknsGQexKd87OgOJOXuQYAO4+PUQ3R5KXOB0DnMgGd28vkJL2Iqr+Wf/89gJoOav7sTV7aN9h7fYYh5wiGnENoHDqicehCTvzQEh/fd6eP8XGnJzgdG8WpmBuMaRSIs50da0Ly1xAs6PQEUempzD+yD4AJjVswtXlbXtyxiWupKfg65d8KnJ6bQ0ZeLs46O14KbMOWSxeIzUynsrsnM1p15EpyInuvXinx8a1btIlXv5/IxROXCD4ayoDne+Do4sC2H/YA8OqSicRHJrLkzVUADJ7WmxGzBjFvxOdEh8caP2lmpmWRlZ5NakKayaJNyC9eEqOSuXah6EL6v8jWuUPNWILiMR9yz0LuaRSXUfkLmzPXAeT/Th+NmvZx/g4u4/Pb6iMAe3DoCE59UVNmFXSqeIC2PGjK5v+sq5r/X0NsobVR1rbuk428tmwyF46FEXIklP4v9cw/95buAuC1ZVOIi0xgycyV+aHa6ahcL//7UezsdfhUKEP1gCpkpmURGZb/BziwWwAoCtdCIilfoxzj5j/D1eDrxj5L0i8bjjHj5ScIuRhF0IUbDOobiJOjHZt35H+Qmjn1CeLi0/jmh70APD2wBWOGt+OdDzcSFZ2Ct2d+7sjMyiEzKxetVsM7M/pSq7of099eh1ajMbZJScskL89QouNbd3U3r9Z9moupVwlOjWBAxY44auzZdiP/EvWrdYYRn53MkssbARjs34URVXsw7/xyorMSjLMcmfpssvQ5ZOizOZUUynPV+5BtyCUmK4GGnjXo6hfI12G/FRWGRdzzl19NnTqVkSNHEhgYSIsWLVi4cCHp6emMHj3aGvGZ2Hz+At7OTrzQsTW+rs4ERcfy7Mr1xKfn/wF9xN3NZHHeU80aYa/T8fmg3ib9fLbnEJ/tPYxeNVDbz4f+AfVwc3QgJjWNA5ciWLj7ILn6kr91T5+1EVK80bm+bPzyq+yEkcYEpWgrwG2Vp9Z5OIrigIPXVyb95KYuJC9tIQCG7G3kJr+BznUSinY2at4lchInYsg9VlLDMtoYFkIZR2emNm+Lr7ML5+NiGLFprXHxZnk3Nwy3jW94/cY4aHV81b2vST+fHDvAwmMH0asqdcr4MrB2fdztHYnOSGPf1St8fHQ/OYaSP3571hzGw8edEW89iVc5Ty6dCueNXvNIislfQFfW3wfVUDC+XuMew97BjrdWv2zSz4p31rLinXUlGntJsGXuIGszqsYbxe3Fm19+FYSa+GzBrdXa8tz+3lIUZ3CfDdpyoGZB3iXU5GmQddt32Dh2QePxgfFHjeciANS0T1HTPrP+mG6z55eDePq6M3LOELzKeRJ28goze7xXcO5VMj33ypT34qt/Cr5fY/C0Pgye1odTu88xrfNsAJw9nHn2/afxqViG1IQ09v/6N0ve+Bl9Xsm/t/7aF4ynhxNjhrfD28uF0EsxTHtrjXHxpp+vu8n4+j7RBHs7He/M7GfSz9KVB1i68gC+ZVxpd/OurKWfmZ5/L8z4mZNnSnY93Z7Yf/Cwd2FE1R542btzKe06b5z+mqTc/A8VZR29TGYdelVoi71Gx1sNxpj0s+LKVlZcyV/U/f75HxhTtRev1x2Om86ZmOxEll3ezMZI6y42VlT13pfIf/7553z44YdERUXRuHFjPv30U1q2NH8/7Z1SUlLw8PCg2sz30Tra4tZN6zs1dpGtQ7CqOr9NtnUIVlf7haIvOT3M8tRcduWtIzk5+QEvKdyf+80dt/JG4oVquLuVzn9doHv5xrYOwaqyn2hu6xCsznFayX9LcUnIS89mV6+vipU37utruqdMmVIiU5ZCiNJFcocQpVvpLPmFEEIIUeKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCZ6snVtT8R2nkoNjZOgTrUmwdgPWpeXm2DsEqVPXhHlfXN8egtXO0dRhW4bwj0tYhWFXENYOtQ7A6142VbR2CVeizs4rdVmYqhBBCCGERUlQIIYQQwiKkqBBCCCGERUhRIYQQQgiLkKJCCCGEEBYhRYUQQgghLEKKCiGEEEJYhBQVQgghhLAIKSqEEEIIYRFSVAghhBDCIqSoEEIIIYRFSFEhhBBCCIuQokIIIYQQFiFFhRBCCCEsQooKIYQQQliEFBVCCCGEsAgpKoQQQghhEVJUCCGEEMIipKgQQgghhEVIUSGEEEIIi5CiQgghhBAWIUWFEEIIISxCZ+sA7tXTgQE826YZvq4uBEfH8s6WXZyJjDbbdlCTBvQLqEdN3zIAnLsRw4K/9pu0n9unGwMa1zfZb1/oFcauXG+9QdyN8zAUl7Gg8YXcYNTUtyH3tPm2ToNRnPqBrlb+z7lnUdMWFG6vrY7i9irYtwC0oA9FTZwChhvWHIlZI+o3YVxAc3ydXAiKj2HWgZ2cio0y23ZonUYMrFWf2t4+AJyJjWb+kb0m7T96tAeDajcw2W/31cuM3LzWeoO4iz6TujNoWh+8y3kSdiqcxS8sIeRoqNm2PcZ24bFnOlKlgT8AF49fYskbPxfZ/sUvn6PX+G588fJS1i/abLUxlFZPdm3MsJ6BlPFw4WJELB8v/4vzl8yfe30fbcgT7etRrWL+uRd8OZovf9lv0n7sgNY81qoOft5u5Or1BF+O5qs1+zkXZr5Pa+tXoS1DKj2Kt70bYWmRfHphPcGpV8227Vm+Jd3KBVLVpRwAF1Kv8V3YZpP2jlp7xlXvSTufBrjbuXAjK55fr+7nj8hDJTKeOz1Tuynj67fMzx0JMcw6sp1T8eZz2NCaAQyo1oDanr4AnEmI4sMTewq1r+5RhtebPkpLP390ioaLyfFM3LOeyPQUq4+nUMxtAhj9aDN83FwIuRHL++t3cfaq+b9tA1s2oE+zetQol/+37fy1GBZt2V9k+7cGdmFw60bM+203P+77x2pjgPuYqdi7dy+9e/emfPnyKIrChg0brBCWeT3q1WJGtw4s3nOY/t/8RHBUHN8PG4C3s5PZ9i2rVGTT2WBGLF/L0CWruJGSypLhAyjr5mLSbm/oZdp+/LXxMfVXGyVsxydQ3Gaipn2OGtcP8oJQvJaAxttsc8W+JWrmRtSEZ1DjB4M+CsVrKWj8ChppK6GU+RnyLqEmDEeN742athjILpEh3a5X9dq82fpRFh0/SK91ywlKiGVFz0GUcXQ22751eX9+Dw1i6B+r6b/hJyLTU1jRcxB+zq4m7XZHXCJw+RfGx/M7/iiJ4RTScXAbxn88kh/fXsPEZtO5dDqcuVvfwNPX3Wz7gI712bVqP692nsOLbd4g9mo887a9SZnyhY93234tqNuyFnHXE6w9DKuwZd4A6NqyNi8O68j36w8x8s0VhEbEsmj6QLzczeeOpnX9+fNQMJPe+4Wxs38mJiGVT6cPxNer4NyLuJHIRz/s5OkZPzDu7VXciEvh0+lP4ulmvk9r6lS2MRNr9uGHK38y7ugnhKVFMr/xODztXM22b+xZg7+i/+Hlf75k8vHPiMlO4sPG4/GxLzhXJ9foQwvvOrx3fiUj//6AdVf38WKt/rTxqW+2T2vqVaUObwZ2ZtGp/fTcuJTziTEs7zqkyNzRyq8Sv185z1N/rmTAluXcSE9hxWND8HMqeD0quXqy9vHhhCXH89SfP/P4H0v47PQBsvV5JTUso8cDavFanw58uf0wgxb+REhkHF8/NwBvV/PnUvPqFdl8MpgxX61l+GeriEpO5ZtxAyjr7lKobZcG1WlUqRzRyWnWHgZwH0VFeno6AQEBLF682Brx3NXo1k355cRZfj11nrC4BGZt2kFWbh4DmzQw237a+q2sPHaa4OhYLsUn8uYf29EoCq2rVjJpl5OnJy49w/hIySr5P7gAivMYyFgNmevyZxNS3gI1E5yeNNteTX4FMldCXhDoL6GmzAQ0YN+6oE/XlyF7D2rafMg7D/oIyP4LDCX/x2lsw0BWBZ1mTchZLibFM3Pvn2Tm5TK4jvnj9+Jfm1hx/iTn42MIS0pg+p5taBSFthUqm7TL1uuJzUw3PlJybHP8Br7ciy3f7WTbst1EBF1j0YRvyM7IofuYzmbbz3vmU/748k/CTl3hakgkC577CkWj0KSL6etRprw3kz8dw9zhi8jLLfmEZwm2zBsAT/Voxm+7zrBx7zkuRyYwb+l2srJz6d2xodn2s77czLodp7gYEUv4jQTe+/ZPNBqFwPoFuePPQ8EcPRdBZGwyl6/Hs+in3bg6O1Cjkm9JDctokH8HNkUeZuuNo4RnRLMgZB1Zhlx6lG9htv1753/it+sHCUuL5GpGDB8F/YKiKDT1rmlsU9+jCtuijnIqKYzorEQ2Rh4mLC2SOu7+JTUso7F1W7Dq4inWhJ0hNDmeNw5vJVOfy+Aajcy2f2n/H/wY8g/nE2MIS0lg+qEtKCi0faSKsc2rTTqw61oY807s5lxCNBFpSey4Fkp8VkYJjarAiI5NWfv3WTYcPc+l6ATeXpf/t61/c/O58fWVW1l98DQhkbFcjk1k1i/5f9ta1TT921bW3YUZ/ToxfeVW8vT6khjKvV/+6NGjBz169LBGLHdlp9FQ/xE/vt5/1LhNBQ5ejqBJxUeK1YeTnQ6dRktyZpbJ9hZVKnLwlfGkZGZx+MpVFu46SNIdbazPDuzqo6Z/dds2FXIOotg1QS1OF4oTKDpQk29tAIdHUdO/y5/x0NUD/bX858jeYfkh3IWdRkND33J8cfJv4zYV2H8tnKZ+5YvVh5NOh51GQ1J2psn2VuX9OT5iEsnZ2Ry8HsFHR/eRlF2yx09np6NWs2qsmldw2UxVVU7sOE29VrWK1YeDsz06Ox2pCQWfKBRFYfry51nz0e+En79m8bhLiq3yBoBOq6FOVT9++OOIcZuqwtFzETSsUbzc4eigQ6vVkJJm/rzSaTX069SI1PQsLobHWiTu4tIpWmq5VeSn8L+M21RUTiRcoL575bvsWcBBa49O0ZKSW/AH9VzyFdr41GdL5BHiclJo7Fmdis6+LA793eJjuBs7jYYGZcrxxdmCyy4qcODGFZr6VihWH05aO5PcoQCdKlbn67N/s7zrYOp5+XEtLZkvzh7iz6sXrTCKoum0GupV8OO7nbf9bVPh8MUIAioX8/y016HTaknOKDg/FQXmPv04y3YfJyw63uJxF8Xqayqys7PJzi745JiScn/XqrycndBpNMSnm1aR8ekZVPPxKlYf07q0JyY1jYOXIozb9oVdYXtwKNeSkvH38mRq57Z8+3R/hixZhUEt1p9yy9B4oSg6VEOc6XZ9PNhXL1YXituroI+B7AM3+yyDonEFl3GoaZ9A6ofg0B7FczFqwjOQe+TuHVqQl2P+8YvLND1+cZkZVPc0f3nnTjNadiQ6PZ0D18ON2/ZcvczWyxe4mppMZXdPXmvRnh+eeJL+G34q0ePn4eOGVqclMTrZZHtiTDL+dYqX+MZ+MJz4yARO7Dhj3DZkel8MeXrWf/q/tYbCUnkDwNPNCZ1WQ0Jyusn2hOQMKj9SvHNv8tAOxCWmc/RcuMn2to2r8e6Unjja2xGXlMbzH6wlOS2ziF6sw8POBa1GS2JOqsn2xJw0KjmXLVYf46v3JC4nmeOJBX9QP72wnlfqDGJNu1nkGfQYUPk4+BdOJ12yaPz/xsvB+WbuMD1+sZnpVHcvU6w+Xm/2KNGZaRy4cQUAH0cXXO0cmNigFR+f3Me847vpWKEaXz06gKf+XMnf0ebXoliDl0v++RmfdsffttQMqpYt3t+2qT3bE5ucxqGLBX/bnu3UHL1e5cf91l1DcSerFxVz585lzpw51n6af/Vc2+Y80aA2I35YQ85t00Cbz10w/v+FmHhCouPY+cIYWlSpyOHLJXdiPTCXceDYEzVhOJBzc+PNq1vZOyFjWf7/5wWBXVMU56dQk0uuqHhQExu3oHf1Ogz5YzXZtx2/P8KCjf8fkhBHUHws+58eR+vy/hy4HmGuq/+kIdP78eiQtkzrNIvc7FwAajatRv8XejKp2Ws2jq7k/VfyBsCI3i14rFVtJr33Czm5plPIx4MieOaNFXi6OtG3U0Pen9KbMbN/IjGlZAuLB/FU5c508mvCyye+INdQcHmtf8X21HWvzMxT3xOdlUgjz2q8WGsAcdkpnEgs2U/zD2Jig1b0rlKXodtWkm3IP36KogCw/dpFvg/KnyE4nxhDU98KDKvVpESLigf1bKfm9Ghcm9FfriEnL3989SqUZXi7Jgxa+FOJx2P1W0pnzJhBcnKy8XH16v0drMSMTPIMBsq4mC7MKePiTFza3a+BjWndjHFtA3n2x18JiYm7a9trSckkpGdQ2cvzvuK8b4ZEVDUPND6m27VlwPAv06nOz6K4jEdNGA15IXf0mYuad8fdBHlhoC3etJqlJGblHz8fJ9Pj5+PkTOwdn0DuNK5RcyY2bsnwTWsITrj7a3E1NZn4zAwquxevwreU5LhU9Hl6vPw8TLZ7lfUgMSrprvs++Upvhk7vx4zu73D5TEEh1KB9HTzLuvNT+JdszVnF1pxVlKtSlvEfjWTFJdusTSgplsobAEmpmeTpDXh7mC5i8/ZwLjR7cadhTwQyoldzXvhgHaFXC+eOrOw8rkUncTbsBu999yf/3959h0dVpQ8c/07LpFdCD70jPXSlKiBKR0BQQUAEAUF2ZRdxV3EtKyuKZZV1VVBQBARkBem9iqAEEkICCSGF9DLJZFKm/f4YSBhIYsCbifB7P8+T54Hh3DvnzZnzznvPPTNYbTaGl7NPo6oYzPlYbVYC3HycHg9w8ybrptWLm40L6cfEBgN48cx/iM0v/WSEm1rL9KYP88ml/3E88zyx+cl8n3SU/WlhjG/QryrCKFd2kela7nAev2APL9ILKx6/Z9p0Y9Z9PXhyzzou5JTmjuwiE2ablYs5zrcFYgyZ1PUqe2N1VcnOd7w+g7xvem/z8SQjt+L3til9uzBtQCgzPt1EdHLp67Nzk3oEenuye/F0zrw9jzNvz6NeoB8vDuvDzpemVkkc11X5SoVer0ev1//u85htNiKSU+nZOIS9UTGA475Yz8YhrPk5rNzjpvcKZeb93Zj29SbCk8v+uM2Navl44+/pQbqx4her8sxgjkDl1hN7yX4HFbj1wm5aXf5hXs+g8pqFPXsqWMLLOOc5VNrGznsytI3AelXR3v8Ws83GufQUetdryK44R5GjAnrXa8iXEb+Ue9yzHboxp1MPnvpxA+cyfnv8ant5E+DuQZrJNTudr7OYLUSfjqXTwHYc2+K48lGpVHQa2I4t/95R7nHjXhzOxJfGsGjI60Sfdl5W3rP6EL/ecCsE4K0dL7NnzSF2rtyvfBB/IErlDQCL1caFy6l0bduAQ6evvfZU0LVtAzbsPlPucU880pWnR3Rn3tsbuXD5t197jvOq0Ok0SnS70ix2K9F5iXQOaM7RDEcOUKGic0BzNicdLfe4CQ36M6nRQBae+ZToPOf9OlqVBp1ae8stRJvdVnKV7ypmm43wzBR61WlUst9BBfSq3ZCvoirIHW27M7tdTybvWc+5TOeP+ZptNs5mJNPE1/n2V2PfQJLynW9hVjWL1cb5pFS6Nw9hX8S19zYVdG8Wwtqj5b+3Pd0vlBkDu/HsfzcRkej8+vzhdCQnLjqv1P7nmdH8cDqS73+OUD6IG9xV31Ox8vgvvD1yMOFX0zh7NYXJ3TvhodOx6Yzjl/T2iMGk5hl5d59jIj3TK5Tn+/XkT5u2k5STS41rqxymYjMmsxlPnY45fXuwM/IiGUYTIYF+vDjwAa5k5XA45kq5/agqdtMXqPyWgjkczGdReU1xbL4s2Ajg+DdrKnbjMscBXjNQec/DnrMArImlqxx2k+MHHJs0/ZdD8c9QfAL0fUA/4NptEtf67NwplvUbytn0FMLSkpnaLhRPnY4NUY5E+G7/oaTk57H05GEAZnboxoKuvZm3dxuJebkEX7tSyTcXY7KY8dTqmB/ai+2x0aSb8mno58+i7n2JM2RzKCHO5fFtfG8rC1fNJvpUDFEnLzFq/iO4e+lLCoCFq+aQcTWLL176BoDxC0fw1JLxvDXpfVLi0gmo5Q9AgbGQwvxC8rKMTps2wVG8ZKVkkxjt2qLwbrd2+2n+/uwQIi+ncD4mhQlDOuOu17H1oOO198qzQ0jPNvLx+iMAPPloV2aM6cXfP/6RqxkGAv0cuaOg0ExBkRl3vZanR/Tg8OkYMnKM+Pt4MPahTgQHeLP3p+hy+1FVNiQc4q+tJxCdl0BkbjxjQ/rgrnFjx1XHLc5FrR8nvcjAZ7GOvTkTGvTn6SZDeCNiDSmF2SWrHAXWIgqtxZisRZzJvsTMZo9SFG0mtTCbDv5NGVQ7lI8vbXF5fJ9FnmRZ70c5l5HMmcxkprUOxVPrxoZLju/kWdb7UVJNeSz99SAAM9t254WODzDv8A8kGg0Eu1/LHRZH7gD4NOIkH/YZwcm0BI6nXKFv3SYMrN+MCbu+cXl8Xx38hTcmDCYiMY3w+BSeeKATHm66kgLgzQmDSTMYWb7d8d42tX8ocwb3ZOHX20nKziXI59p7W5GZgmIzBlOh06ZNAIvVSkZePnHp2VUay20XFUajkUuXSpfTL1++zJkzZwgMDKRBgwYVHPn7bT8fTaCXB8/360mwtyeRqelM/2ZzyebNOn4+TpX1hND2uGm1fDhumNN5Pjx4nI8OnsBqt9GiVg1GdmiDj7uetDwjR2Pief/AMcwu+viNk8IfsasDUfnMu/blV5HYs6eB7doSnaYu3LDmoPJ8HJXKDVXAR06nsRs/wG780PGXot3Yc19B5fUs+P4NLJex58wB82kXBVVqa0wUQe6eLAjtTbCnF+cz0njqx+9KNm/W9XYevyfadkSv0bJi0Ain87x36ijLTx/DarfTKjCYMS3a4uvmTqrJyOHEOJb9fIRim+vH7+D6Y/gH+zJ5yXgCavsTcyaOlx5+g5w0x5VPzQY1sNtK43t05iDc9Dpe+e7PTuf5asl6Vi/Z4NK+V7XqzBsAe36Kwt/XgxljehPk50n0lXTmL91I1rXl5Vo1fJ1ee6MHdsBNp+Wf84Y7nee/m47x2abj2Gx2GtYJZOi8Nvj7eGAwFhIZm8Kzr3/L5STX7bS/bn/aGfx0XkxpMphAN19i8pL4S9h/yTY7itKa7v7YbsgdI+r1wk2tZUm7KU7nWXV5J19e3gXAaxFreKbpUBa3nYSv1pPUwmw+j/2R/yW5/suvtsZdIFDvyQsdHyj58qvJe9eRce3jn/W8fLHfmDtadnbkjn6jnM6zPOwIy8MchePOhGgW/7ST5+7rwatdHyQ2N4tZBzdzKs31n7LaERZNgLcHcwb3pIaPJxeupjPzs80lmzfrBDjnxvE9He9tyyc7v7d9vOs4H+864dK+30xlt9/eFvkDBw7Qv3//Wx6fPHkyq1at+s3jc3Nz8fPzo+miN9G4u9/OU981Imd8XN1dqFKNt8yo7i5UuRaz7p5NrLfDYjdzgC0YDAZ8fV1371ipvNFl3OtodPdm3vCcem+vPl1OdP33d7ia91llbtn90ViLCrnw0UuVyhu3vVLRr18/brMOEUL8Pyd5Q4j/H+Q/FBNCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCK01fXERcEW1B6W6nr6KjW0/cDq7kKV0j+vqe4uVLmPrxyp7i5UCWOejc5tq7sXd67e9Bh0Xm7V3Y0qkfp20+ruQpXybVxtbzcuk9/DVN1dqBI2U2Gl28pKhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUIUWFEEIIIRQhRYUQQgghFCFFhRBCCCEUoa3uDtyup9p2YkbHrgR7eBGZmcYrR/cSlpZSZtsJrdszpkVbWgbWAOBceipLTx5yav9O/4d5rOV9TscdiL/M5B+/q7ogKjBsal/GPjeIgJq+xEYk8vFL64j+Na7MtkOeuJ8Hx3WnYau6AFw6G8/KN7aU237uvybyyOQ+rHh5Pd9/uq+KIqjYpC4dmNYzlGBvLy6kpvOPnfs5e7Xs8RvXqR0j27WmebBj/CJSUnl3/1Gn9v8cNpjRHdo6HXcoJo7pazdVXRAV8POeQoDPc2g0wRQXnyctZzFFxWfKbOvrNQlfr8dw07UEoKj4LBmGt25pr9M2p4b/Yjz0PVGhpdgSTXLGdCzWpCqO5t4ypHZfhtcdhL+bL1fyE/n88jouGePKbPtgzfvpW7M7IZ6OuRVrjOeb+C1O7d3VeiY1HEW3wA54a71IK8pke/I+dqUedkE0txo1tBOPj+xKYIAXMXFpLP90L5EXy55bwx5qz+D+bWnS0DG3omJS+XT1oZL2Go2aZybdT48uTahb2498UzGnwq6w4quDZGbluyymG42/vwNTBnShho8X0VfTeWvjfsLjU8tsO6bHfQzr2oZmdYIAOJ+QxgfbjpTb/uXHBjKud3uWbj7AmoO/VlkMFXmiWReead2DYHdvInNSWXJ6F2ezrpbZdnyTjoxq3I4WfsEAhGel8M7ZA7e0b+obxMIOA+ge3ACNWs0lQwbPHd1Isim3yuK4rZWKt956i65du+Lj40PNmjUZOXIkUVFRVdW3WzzatCUv9+rH+6eO8ejGr4jMTGf1I48R5O5ZZvuedUP436VIJvxvHaM2f81VYy6rH3mMWl7eTu0OxMcS+uXHJT9z9/zginBu0WdEF55ZMpY172xlzoNvEhuRyBvr5uJXw6fM9u17t+DA5lP8ZfR7vDB0KelJ2by5/nmCavvf0rbX0I606tKYjOScqg2iAkPbtGDRQ3356PAJRn62hgup6Xz++GgCPT3KbN+tYX22RkTx1JoNjF+1luTcPL6YOJpaPs7jd+jSZXq9t6LkZ8Hmba4I5xbeHsOp4f8qWbnLSEgZTJH5PPWC16JRB5XZ3kPfizzTZhLTxpKQOgyL9Sr1gr9Fo6ld0kanaUhIze8pNl8iKW0M8SkDyDK8h91e6KqwFFHduaNXUBcmNxrLhsStLAx7k7j8RF5uMxdfXdlzq61fC45knOLV8Pd46dxSMoqz+Vub5wl08y9pM7nRWDr6t+GDiyuZf2YJ25L3Mq3JBEID2rsoqlID7m/JnKn9WLXuGNMXfMWly+kse/Ux/P3Kzo0d24Ww53Akz7+8jpkLvyYtI5dlrz5GjUDH3HLXa2nRtBZfrj/OtAVfsfit72lQL4B/Lh7tyrBKDO7UghdH9mHFjhOMf+dropIyWDFzNIHeZeeO0Gb12f7LBab9+zueWP4tKTl5rJg1mpp+Xre0HdCuKe0b1SY1x1jVYZTrkZDWvNTpQT4IP8zwnZ9zISeNVf0mEKQve/y612zID1fOM2nf14zd/SXJply+7Pc4tTxKX88NvP1ZN/ApYnMzmbhvDY/s+C8fRRyh2Gqp0lhuq6g4ePAgs2fP5sSJE+zevRuz2cygQYPIz3dN5Tq9fSjfRp5lQ1Q4F7MzeenQLgosZsa1uq/M9vP2bmN1xBnOZ6YRk5PFXw7uRK1S0bteQ6d2RVYr6QX5JT+5xUWuCOcWo2c+yI41R9n97XHio5P58MVvKCowM/jxXmW2XzrrC7auPEhseCKJl1JZ/sJqVGoVHfu0dGoXVNufWW+OZ+msL7Cara4IpUxPd+/C+l/D2RQWQUxGFn//cQ+FZgtjO5Y9fn/+fjvfnA4jMjWd2MxsFm/djVqlomejEKd2xVYrGfmmkp/cwuoZvwCfZ8k1fk1u/jqKLdGkZS/EbivA1+vxMtunZs3GYPySYnMEZsslUrP+BKjx1D9Q0ibI/6/kF+4j0/A6ReZwzNYr5BfuwmrLdFFUyqju3DGs7oPsST3K/rTjJBYk82nsNxRZzQyoWfbcev/iF+xMOUicKZGrBamsuLQaFSra+ZXOrZa+TTiYfoKI3GjSizLZk3qEuPxEmnk3cklMNxo/IpQfdp3lx73hxCVk8s4nuygsMvPIg2XPrX+8u43vt5/h0uU04pOyePujnajVKrp0cOTGfFMxC17ZwP6jUSQkZXM+Opn3/rOXVs1qU7Oci5yq9FS/zmw8Hs6Wk+eJTc3iHxv2UFBsYWT3suNbtGYH646eJSopnbi0bF791pE7urdo4NSupp8Xi8b0Z9HqHVhs1Zcbp7bqzrqYM2y8fJZLuRm8/POPFFgsjG3Socz2C05s4etLp4nMSSU2L5NFP29DpVLRq1ajkjZ/atePA8kxvB22j/M5qcQbc9h79SKZRaYqjeW2ioodO3YwZcoU2rZtS4cOHVi1ahXx8fGcPn26qvpXQqdW0y64NkcSr5Q8ZgeOJF6hc626lTqHh1aLTq0mp7DA6fEedUM4Pfk59k2YxusPPIS/3l3JrleKVqeheYcG/HoosuQxu93Or4ciaR3apFLn0Hu4odVqyMsufdGoVCpe/PcUvvv3bq5EJSve78rSqdW0rVOLY5edx+9Y3BU61qtTqXN46LRo1RpyCpyv0rs1rM/xF2ayY9YUXn14IP4erh8/0KF3a4+p6MalbzumosO467tU6gwqlQcqtNhs2dcfwcv9QcyWWOrWWEvjuucIqbkNL48hive+qlVn7tCqNDTxbsBZww1zCzvnDJG09Knc3HJTu6FRaTBaSudWVG4soYHtS1Yv2vq2oK5HLcIM5xXt/2/RatW0aFqb02E3zC07nAq7QtuWlcuNer0WrUZNXl5BuW28vPTYbHaM+a4t2rUaNa3r1+JEdHzJY3Y7/BQdT4dGlcsd7m6O3GHIL80dKhW8OWkIq/adJial+op0nVrNfQF1OJZ6ueQxO3As9TKdgupX6hweGh06lZqcYsf4qYB+dZsRl5fFyr4TODlyPhsfmsJD9VpUQQTOfteeCoPBAEBgYGC5bYqKiigqKn0R5ube2b2cAHcPtGo1GQXOVVZGgYmm/uU//40W9ehLan4+R5NKJ9/B+MvsiI0mIc9AQ19/FnZ7gC8fGcuozV9js9vvqK93wjfQG41WQ0668+8nJz2PkGa1yznK2dS/jyYz1eBUmIybOwir1caW/1bPHorrAjyvjV/+TeNnNNEkqHLj9+cBD5BmNHLscmlyORwTx64LF0nMyaVBgB8L+t/PZxNGM27VWpeOn0YdiEqlxWpNd3rcYk3HU9usUueo4f8yFlsqpsLD185ZA7XamwCfOWQa3ibD8Dpe7v2pE/Q5SeljKSg6rngcrvJbuUOpvAHgo/VGo9JgKL5pbpnzqOdRubn1RKPRZJsNnM0pnVufX17HzKaT+DT0n1hsVuzYWBGzhsjcS3fc1zvh5+uBVqMmK8d5bmXnmGhYv3Jza9ZTfcnIyufUDYXJjdx0GmY91Yc9hyMxFRT/7j7fjgAvR3yZec7xZeaZaFwroFLneGHYA6TnGp0Kk6kDu2Kx2fn6UPXsobguwM3TkRsLnVftMgrzaeJb9q3Tmy3sMIDUQiNHUxyFSZC7F946Pc+27sm7Zw+yNGw/feo04eP7xzJp3xpOpsf/xhnv3B0XFTabjfnz59O7d2/uu6/sJShw3EtdsmTJnT6NYmZ17Mawpq0Y/791FFlLl7l+iLlQ8ueorAwiM9M5MmkGPeuGcDSp6n7xShs3dzD9RoaycNS7mIsc98yatW/AiBkDmDPwzWru3e83o1dXHmnbiidXr6f4hvHbdr70vnx0egZRaRnsnTON7g3rczwuoTq6ekcCfObg4zGCxPQx2Ln+ZupYSMwv2EGO8VMAis0RuOtD8fN68q4tKiqTO/4oeQNgZL3B9A4K5dWIdzHbS+9HD63Tn+Y+jXkr8t9kFGXR2rc505s8TlaxgXOGCxWc8Y9l0phuDHygFc8vXkdxGbdHNRo1SxYOR6VSseyT3dXQw99n6sCuDOnUkqkfbaDY4oivdf2aTOrTifHvfF3Nvfv9nm3dk0cbtGHivjUUX7uFo0YFwJ6kaFZGnwQgMieVzjXqM7FZ5z9mUTF79mzCw8M5cuRIhe0WLVrEggULSv6em5tLSEhIBUeULbuwAIvNRg0P540rNTw8STdVfF92RoeuzOrUnUlb13MhK73Ctgl5BjILTDT0DXBpUZGbZcRqseIf7Ov0uH+wD9lpFV+ljXnuIcY9P5hFY5dz+XzpJwLu69EM/xo+rP61tKjQaDU8s2Qso2YMZHLoYmWDqEC26dr4ed00ft6epBsrHr+pPbowo1dXpny9kai0jArbJuQYyMo30SDQ36VFhdWWhd1uQaMJdnpcqwnGYkur8Fh/n5kE+M4hKW08xebSK2HHOc0UWS46tS82X8RD3025zrtYZXKHUnkDIM9ixGq34ud209zS+ZBjrnhuDa/7EKPqDea1iOVcMZXOLTe1jscbjOBfUSv4JTscgCumJBp51Wd43YdcWlQYcguwWG0E+jvPrQB/TzKzK55bE0Z2ZdLo7rzwynpirtyaGzUaNa8tHE7tYF/m/W2dy1cpALLzHfEF+TjHF+TjSUZuxfsDJvfvwtQHQ5nx8SYuJpfmji5N6xHo7cnOV6aXPKbVqPnTiD5M6tuJh1/7QtkgKpBdbHLkRnfnTaQ13L1IL6h4/Ka37M7M1r14av83RBlK80x2sQmzzcolg3O+jMnNILTGnc2jyrqjomLOnDls3bqVQ4cOUb9+xfd89Ho9er3+jjp3I7PNxrn0FHrXa8iuOMfyogroXa8hX4b/Uu5xz3bsxpxOPXhq2wbOpZf9caIb1fbyJsDdgzSTa3cCW8xWLobF0/GBVhzfHgY49kN0fKAVP3x+oNzjxs4ZxOPzH2bx+A+4GOZcBO3d8BO/HnJObm+se569G06we61rr3LNNhsRyan0bNyAPdExgGP8ejZqwJpTZ8o9bnrPUGb17s7UtZsIT/7t8avl442/pwfpea7+2JuZouKzeOrvJ79gx7XHVHjo78dgXFnuUQE+zxHgO4+r6Y9TZA675ZyFxWdw0zZ1etRN2xSLJVHZ7rtIZXOHUnkDwGK3EmuMp51fK37Ouja3UNHOrxXbUw6Ue9yIuoMYXf9hXj//ATH5znNLo9KgU2ux33SLzWa3oVapFOl3ZVksNqJjUujSviGHf7qWG1XQpX1DNv1Yfm6cOKobTz7Wgz+9uoGoS7fOresFRf06/sx7eR25edXziSOL1UZkYirdm4ew/9y13KGC7i1CWHv45jlT6ukBoUx/qBuzVmzifIJzfD/8HMmJKOcx/WTmaLaeimTLyQjlg6iA2WYjPDuZXrUasTspGriWG2s1YvXFU+UeN6NVD55r05spB9dyLtt5v5zZZuNcVjKNb7p90tgniCSTQfEYbnRbRYXdbmfu3Lls3ryZAwcO0Lhx46rqV5k+O3uKZf2HcjY9hbC0ZKa2D8VTp2NDlONK4d3+Q0nJz2PpScc96Zkdu7Gga2/m7dlGYl4uwR6OSjDfXIzJYsZTq2N+aC+2x0aTXpBPQ19/FvXoS5whm0MJcS6NDWDTij38+cMpXAy7QtQvcYx6dgDunm7s+vYYAH/+aAqZyTmsfON7AB6bO4gnFw7j7VlfkJqQSUBNx5VYQX4RhflF5GXnk3fTlYrVbCU7LZfEmN9+g1bayp9O8/bwIYQnp3I2KYXJ3TvjodOxMcwxiZcOH0JqnpFl+x1XsM/07Mq8vj1Z8P12knIMJascpmIzJrMZT52OOX16svPCRTKM+TQI8OPFgX24kpXD4diy7w1Xpey8/1Ar6H0Ki8MoLD5DgM8zqNWe5OZ/C0CtwA+wWFPINDhWjgJ8ZhPo9yKpmbMxWxLQqB2rHDZ7Pna76do5P6FO0AoKik5QUHQUT/f+eHk8RGLaGJfH93tUd+744eoe5jSfQozxCpeMcTxSZwB6jRv70xxza26zKWQW5/BN/PcAjKw3iPEhw1ge/QXpRZn46xxzq9BaRKGtiAJrIRGGaJ5sNJriWDPpRZm08W1B3+AefBnn+u+4WbflFC/NG8qFSylEXkzmsWGheLjr+HGPIzcunj+UjMw8/rPakRsnju7GtIm9eW3ZNlLScgn0d+TGgsJiCgrNaDRq/vGX4bRoWou//GMTarW6pE2usQCLxebS+L468AuvTxzM+YQ0zsWn8ETfTni46fj+J0fueGPSYFINRj7YehSApweGMvvhnvz1q+0kZeWWrHKYiswUFJsxmAoxmJyLJIvNSmZePnFp2bjaFxd+4l89hnMuK5mwrKs83aIbnlod38WeBeCd7sNIKcjjnbMHAJjRqifz2/XhhePfk5hvKFnlMFkc720A/408wfu9RvFzWjwn0q7Qp05TBtRtzsR9q6s0ltsqKmbPns0333zDli1b8PHxISXF8UUpfn5+eHiU/XlhJW2NiSLI3ZMFXXsT7OnF+Yw0ntr2Xcnmzbo+PtgovXJ4om1H9BotKwaPcDrPe6eOsvzUMax2O62CghnTsi2+bu6kmowcTohj2c9HSu5NudKhLafxC/LhyYXDHF9+FZ7IyxM+JCc9D4Ca9QKx20rje3RyX9z0Ov72xbNO51nzr62s+ddWl/a9Mn48H02gpyfP9+1FsJcnkanpTFu7icxrmzfr+Pk4ba58vEt73LRaPho7zOk8Hx46zoeHjmO122lZswaj2rfBx11PWp6Ro7FXWH7wGGar68fPWPA/NDlBBPktvPblVxEkpU/EanMsQWo19YDSZOznPRm1Sk+dGp85nSfT8A5ZucsAyC/YTlr2XwjwmUuw/z8wW2JIzphOYfFJl8WlhOrOHccyT+Or82FCg2H463yJy0/kjfMfYjA75lYNfaBT7hhUqy86tY4XWznPrfUJW1mf4Jhb70V/xsSGI3m++VS8tZ5kFGWxNn4Lu1IPVXk8N9t3JAp/X0+mTexNYIAXly6n8ecl35FtcMytWjV8nHLHyCEdcdNpef2vzrnxi7VHWfntMYKDvHmge3MAVr0/xanN3MXfcibctfuVdv4aTYCXB8893JMavp5EJaUz6z+byTI64qsd4Jw7xvV25I53pzrnjk92HOeTHSdc2vfK2JYQSaC7F/Pb9aWGuxeROak8feBbMoscF4V1vPycXp+TmndGr9Hy8f1jnc7zfvghPgh3FI67kqL426ntzGrTi793HkRsXhazj27kdEbVrnKq7Dev31XUuJxlvZUrVzJlypRKnSM3Nxc/Pz/qv/sa6mr56F/Va/33mOruQpWKfb7qP5ZU3bZN/ld1d6FKGPNsdG6bhsFgwNfX97cPUMjvzR3X88bwXU+j83JTuHd/DKlvN/3tRncxQ+O77gucb1t+j6r9DojqYjMVEjft9Urljdu+/SGEELdLcocQ/z/IfygmhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRUhRIYQQQghFSFEhhBBCCEVIUSGEEEIIRWhd/YR2ux0AW2Ghq5/aZSy24uruQpWy3sNjd50xz1bdXagSRqMjruvz8G5xvb/m/Ht3blnM9/a8sha5/O3G5Wyme3MMbQVFQOXyhsru4uySmJhISEiIK59SCHGThIQE6tevX93dqDTJG0JUv8rkDZcXFTabjatXr+Lj44NKpary58vNzSUkJISEhAR8fX2r/PlcTeK7u7k6PrvdTl5eHnXr1kWtvnvufkreUJbEd3f7I+cNl69HqdXqarlC8vX1vSdfXNdJfHc3V8bn5+fnkudRkuSNqiHx3d3+iHnj7rlUEUIIIcQfmhQVQgghhFDEPV9U6PV6XnnlFfR6fXV3pUpIfHe3ez2+u9W9Pi4S393tjxyfyzdqCiGEEOLedM+vVAghhBDCNaSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQipKgQQgghhCKkqBBCCCGEIqSoEEIIIYQi/g+0KCWttjcdHgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plt.subplots() 用于创建子图网格，其维度基于 outputs.attn_scores.shape[:2]。子图的行数和列数似乎由 outputs.attn_scores 的前两个维度确定。\n",
    "fig, axis = plt.subplots(*outputs.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs.attn_scores.shape[1]):\n",
    "        # axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())：此行使用 Matplotlib 的 matshow 绘制每个 i 和 j 的注意力分数热图。detach().numpy() 将 PyTorch 张量转换为 NumPy 数组以进行可视化。\n",
    "        axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs.attn_scores.shape[2]):\n",
    "            for y in range(outputs.attn_scores.shape[3]):\n",
    "                # axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")：此代码在热图上叠加文本，显示 (x, y) 位置处的注意力分数。格式化部分 f\"{outputs.attn_scores[i, j, x, y]:.2f}\" 确保以两位小数显示注意力分数。文本以白色居中显示在 (y, x) 坐标处。\n",
    "                axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention without mask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "42dc1608bbcd426c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.480546Z",
     "start_time": "2025-02-06T14:04:30.233690Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.521350Z",
     "iopub.status.busy": "2025-02-07T15:37:43.521045Z",
     "iopub.status.idle": "2025-02-07T15:37:43.763598Z",
     "shell.execute_reply": "2025-02-07T15:37:43.763089Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.521321Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# mask\n",
    "mask = torch.Tensor([[0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]]).reshape(1, 1, 3, 4)  #手工构造mask\n",
    "outputs_masked = mha(query, key_value, key_value, mask)\n",
    "\n",
    "fig, axis = plt.subplots(*outputs_masked.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs_masked.attn_scores.shape[1]):\n",
    "        axis[i, j].matshow(outputs_masked.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs_masked.attn_scores.shape[2]):\n",
    "            for y in range(outputs_masked.attn_scores.shape[3]):\n",
    "                axis[i, j].text(y, x, f\"{outputs_masked.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\",\n",
    "                                color=\"w\")\n",
    "fig.suptitle(\"multi head attention with mask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd2b7a6a479f877c",
   "metadata": {},
   "source": [
    "### Transformer-Block"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "2247e4861550df31",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.489814Z",
     "start_time": "2025-02-06T14:04:30.481541Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.764453Z",
     "iopub.status.busy": "2025-02-07T15:37:43.764251Z",
     "iopub.status.idle": "2025-02-07T15:37:43.774498Z",
     "shell.execute_reply": "2025-02-07T15:37:43.773966Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.764430Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerBlockOutput:\n",
    "    # 用于存储某个块产生的隐藏状态。\n",
    "    hidden_states: Tensor\n",
    "    # 包含了自注意力机制（self-attention）所计算得到的注意力分数\n",
    "    self_attn_scores: Tensor\n",
    "    # 是一个可选字段，存储了交叉注意力（cross-attention）计算得到的注意力分数。\n",
    "    # 这里的 Optional 表示这个字段可以是 Tensor 类型，也可以是 None。\n",
    "    cross_attn_scores: Optional[Tensor] = None\n",
    "\n",
    "\n",
    "class TransformerBlock(nn.Module):\n",
    "    def __init__(self, config, add_cross_attention=False):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]  # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"]  # 多头注意力的头数\n",
    "        dropout_rate = config[\"dropout\"]  # 随机失活率\n",
    "        ffn_dim = config[\"dim_feedforward\"]  # 前馈网络的维度\n",
    "        eps = config[\"layer_norm_eps\"]  # 层归一化的epsilon值\n",
    "\n",
    "        # self-attention\n",
    "        self.self_attn = MultiHeadAttention(config)  # 多头注意力\n",
    "        self.self_ln = nn.LayerNorm(self.hidden_size, eps=eps)  # 层归一化(层标准化)\n",
    "        self.self_dropout = nn.Dropout(dropout_rate)  # 随机失活\n",
    "\n",
    "        # cross-attention，交叉注意力，decoder中使用,因此额外做一个判断\n",
    "        if add_cross_attention:\n",
    "            self.cross_attn = MultiHeadAttention(config)\n",
    "            self.cross_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "            self.cross_dropout = nn.Dropout(dropout_rate)\n",
    "        else:\n",
    "            self.cross_attn = None\n",
    "\n",
    "        # FFN,前馈神经网络\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(self.hidden_size, ffn_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(ffn_dim, self.hidden_size),\n",
    "        )\n",
    "        self.ffn_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "        self.ffn_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "    def forward(\n",
    "            self,\n",
    "            hidden_states,\n",
    "            attn_mask=None,\n",
    "            encoder_outputs=None,\n",
    "            cross_attn_mask=None,\n",
    "    ):\n",
    "        # self-attention,自注意力\n",
    "        self_attn_outputs = self.self_attn(\n",
    "            hidden_states, hidden_states, hidden_states, attn_mask\n",
    "        )  # self_attn_outputs 是 AttentionOutput 类型\n",
    "        self_embeds = self.self_ln(\n",
    "            hidden_states + self.self_dropout(self_attn_outputs.hidden_states)\n",
    "        )  #多头注意力进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        # cross-attention，交叉注意力\n",
    "        if self.cross_attn is not None:\n",
    "            assert encoder_outputs is not None, \"encoder_outputs should not be None\"\n",
    "            cross_attn_outputs = self.cross_attn(\n",
    "                self_embeds, encoder_outputs, encoder_outputs, cross_attn_mask\n",
    "            )  # query是self_embeds，key和value都是encoder_outputs\n",
    "            cross_embeds = self.cross_ln(\n",
    "                self_embeds + self.cross_dropout(cross_attn_outputs.hidden_states)\n",
    "            )  # 交叉注意力进行dropout，然后和self_embeds进行残差连接，然后进行层归一化\n",
    "\n",
    "        # FFN\n",
    "        # 如果有交叉注意力，则使用交叉注意力的输出作为FFN的输入；否则，使用self_embeds作为FFN的输入\n",
    "        embeds = cross_embeds if self.cross_attn is not None else self_embeds\n",
    "        # embeds:[batch_size, seq_len, hidden_size]\n",
    "        ffn_output = self.ffn(embeds)  # 前馈神经网络\n",
    "        # ffn_output:[batch_size, seq_len, hidden_size]\n",
    "        # 前馈神经网络进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "        embeds = self.ffn_ln(embeds + self.ffn_dropout(ffn_output))\n",
    "\n",
    "        return TransformerBlockOutput(\n",
    "            hidden_states=embeds,\n",
    "            self_attn_scores=self_attn_outputs.attn_scores,\n",
    "            cross_attn_scores=cross_attn_outputs.attn_scores if self.cross_attn is not None else None,\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19be62a364cb5cc2",
   "metadata": {},
   "source": [
    "## Encoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "4f0e09edb3e7bbfe",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.497908Z",
     "start_time": "2025-02-06T14:04:30.490811Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.775418Z",
     "iopub.status.busy": "2025-02-07T15:37:43.775154Z",
     "iopub.status.idle": "2025-02-07T15:37:43.781635Z",
     "shell.execute_reply": "2025-02-07T15:37:43.780680Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.775373Z"
    }
   },
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class TransformerEncoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    attn_scores: List[Tensor]\n",
    "\n",
    "\n",
    "class TransformerEncoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_encoder_layers\"]\n",
    "\n",
    "        # layers,仅仅是一个模块的列表，它本身没有定义前向传递（forward pass）过程。你需要在 forward 方法中明确地定义如何使用这些模块。\n",
    "        self.layers = nn.ModuleList(\n",
    "            [TransformerBlock(config) for _ in range(self.num_layers)]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "            self, encoder_inputs_embeds, attn_mask=None\n",
    "    ) -> TransformerEncoderOutput:\n",
    "        # 存储每个层的注意力分数\n",
    "        attn_scores = []\n",
    "        # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        embeds = encoder_inputs_embeds\n",
    "        for layer in self.layers:\n",
    "            # layer就是一个TransformerBlock(config)\n",
    "            block_outputs = layer(embeds, attn_mask=attn_mask)\n",
    "            # 在每个层的输出中，提取了隐藏状态 block_outputs.hidden_states，\n",
    "            embeds = block_outputs.hidden_states  # 上一层的输出作为下一层的输入\n",
    "            # 并将对应的注意力分数 block_outputs.self_attn_scores 添加到列表 attn_scores 中。\n",
    "            attn_scores.append(block_outputs.self_attn_scores)  # 存储每个层的注意力分数,用于画图\n",
    "\n",
    "        return TransformerEncoderOutput(\n",
    "            last_hidden_states=embeds, attn_scores=attn_scores\n",
    "        )\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a142b31a53d27997",
   "metadata": {},
   "source": [
    "## Decoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "c0e7f64561cbd5f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.505801Z",
     "start_time": "2025-02-06T14:04:30.498905Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.782627Z",
     "iopub.status.busy": "2025-02-07T15:37:43.782308Z",
     "iopub.status.idle": "2025-02-07T15:37:43.788753Z",
     "shell.execute_reply": "2025-02-07T15:37:43.788277Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.782595Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerDecoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    self_attn_scores: List[Tensor]\n",
    "    cross_attn_scores: List[Tensor]\n",
    "\n",
    "\n",
    "class TransformerDecoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_decoder_layers\"]\n",
    "\n",
    "        # layers\n",
    "        self.layers = nn.ModuleList(\n",
    "            [\n",
    "                TransformerBlock(config, add_cross_attention=True)\n",
    "                for _ in range(self.num_layers)\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "            self,\n",
    "            decoder_inputs_embeds,\n",
    "            encoder_outputs,\n",
    "            attn_mask=None,\n",
    "            cross_attn_mask=None,\n",
    "    ) -> TransformerDecoderOutput:\n",
    "        self_attn_scores = []  # 存储每个层的自注意力分数\n",
    "        cross_attn_scores = []  # 存储每个层的交叉注意力分数\n",
    "        # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        embeds = decoder_inputs_embeds\n",
    "        for layer in self.layers:\n",
    "            # layer就是一个TransformerBlock(config, add_cross_attention=True)\n",
    "            # 为什么交叉注意力的mask要传入cross_attn_mask而不是attn_mask？\n",
    "            # 因为计算MutilHeadAttention的时候,keys和values都是encoder_outputs，query是decoder的输出，因此需要传入cross_attn_mask。\n",
    "            block_outputs = layer(\n",
    "                embeds,\n",
    "                attn_mask=attn_mask,  # 自注意力的mask\n",
    "                encoder_outputs=encoder_outputs,\n",
    "                cross_attn_mask=cross_attn_mask,  # 交叉注意力的mask\n",
    "            )\n",
    "            embeds = block_outputs.hidden_states  # 上一层的输出作为下一层的输入\n",
    "            self_attn_scores.append(block_outputs.self_attn_scores)  # 存储每个层的自注意力分数\n",
    "            cross_attn_scores.append(block_outputs.cross_attn_scores)  # 存储每个层的交叉注意力分数\n",
    "\n",
    "        return TransformerDecoderOutput(\n",
    "            last_hidden_states=embeds,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores,\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c345d9c0e326a13",
   "metadata": {},
   "source": [
    "#### mask\n",
    "\n",
    "- mask实际上大类上只有两种\n",
    "    1. `padding_mask`：mask掉`pad_idx`，不计算损失\n",
    "    2. `attention_mask`：mask掉`pad_idx`，不计算注意力分数\n",
    "- Decoder的`attention_mask`和Encoder有一定的区别：\n",
    "    - Encoder可以同时看见序列所有信息，故只mask掉`pad_idx`\n",
    "    - Decoder只能看到在自身之前的序列的信息，故要额外mask掉自身之后的序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "9baa438dbfbcdd8a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.512835Z",
     "start_time": "2025-02-06T14:04:30.506798Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.789547Z",
     "iopub.status.busy": "2025-02-07T15:37:43.789371Z",
     "iopub.status.idle": "2025-02-07T15:37:43.794298Z",
     "shell.execute_reply": "2025-02-07T15:37:43.793795Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.789527Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[False, False, False, False, False],\n",
      "        [ True, False, False, False, False],\n",
      "        [ True,  True, False, False, False],\n",
      "        [ True,  True,  True, False, False],\n",
      "        [ True,  True,  True,  True, False]])\n",
      "--------------------------------------------------\n",
      "tensor([[False,  True,  True,  True,  True],\n",
      "        [False, False,  True,  True,  True],\n",
      "        [False, False, False,  True,  True],\n",
      "        [False, False, False, False,  True],\n",
      "        [False, False, False, False, False]])\n"
     ]
    }
   ],
   "source": [
    "print((torch.triu(torch.ones(5, 5)) == 0))\n",
    "print(\"-\" * 50)\n",
    "# 符合Decoder的attention_mask形式\n",
    "print((torch.triu(torch.ones(5, 5)) == 0).transpose(-1, -2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "52343e686b4eed69",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:30.667599Z",
     "start_time": "2025-02-06T14:04:30.513830Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.795048Z",
     "iopub.status.busy": "2025-02-07T15:37:43.794837Z",
     "iopub.status.idle": "2025-02-07T15:37:43.948504Z",
     "shell.execute_reply": "2025-02-07T15:37:43.947907Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.795026Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 480x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def generate_square_subsequent_mask(sz: int) -> Tensor:\n",
    "    # The masked positions are filled with True.\n",
    "    # Unmasked positions are filled with False.\n",
    "    # torch.ones(sz, sz): 创建一个全为 1 的 sz × sz 的矩阵。\n",
    "    # torch.triu(...): 使用 triu 函数取得矩阵的上三角部分，将主对角线以下部分置零。\n",
    "    mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "    return mask\n",
    "\n",
    "\n",
    "#画出一个16×16的矩阵热力图，黄色部分为True，是掩码\n",
    "plt.matshow(generate_square_subsequent_mask(16))\n",
    "plt.colorbar()\n",
    "plt.xlabel(\"keys\")\n",
    "plt.ylabel(\"querys\")\n",
    "plt.title(\"1 means mask while 0 means unmask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "2548d49c0f7a8fd5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.199068Z",
     "start_time": "2025-02-06T14:04:30.668599Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:43.949385Z",
     "iopub.status.busy": "2025-02-07T15:37:43.949190Z",
     "iopub.status.idle": "2025-02-07T15:37:44.464323Z",
     "shell.execute_reply": "2025-02-07T15:37:44.463651Z",
     "shell.execute_reply.started": "2025-02-07T15:37:43.949364Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['[BOS]', '[UNK]', 'quick', 'brown', '[UNK]', 'jumps', 'over', 'the', '[UNK]', 'dog', '.', '[EOS]']\n",
      "--------------------------------------------------\n",
      "tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=torch.float64)\n",
      "--------------------------------------------------\n",
      "tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=torch.float64)\n",
      "--------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAG1CAYAAADz+MUUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAASMZJREFUeJzt3X1cVHX+///ncCEiyECGGkpeYWamqUhayoV5gVSu1q5iZoraupm22mptfmxTu5A1s/pWm+Zamkapu2vZthmkiXhV6iaJpa6GEshaXjKocX1+f/Rz1hFUDsLMAI/77XZuwZkz7/frDM68es45c8ZiGIYhAAAAAEClebi6AAAAAACobQhSAAAAAGASQQoAAAAATCJIAQAAAIBJBCkAAAAAMIkgBQAAAAAmEaQAAAAAwCSCFAAAAACYRJACAAAAAJMIUsBlLFu2TBaLRUeOHHFYP3/+fLVt21aenp7q2rWrS2qrD2bPni2LxaITJ064uhQAKOdyPaK6JCQkyN/fv0bGrsiF/dm1a5fT5ryaC32gtqKP1X0EKcCElJQUPfnkk+rdu7eWLl2quXPnVuv427Zt0+zZs3XmzJlyt82dO1cfffRRtc4HAACAqiFIASZ88cUX8vDw0Ntvv63Ro0fr7rvvrtbxt23bpjlz5hCkAAAA3BxBCjDhp59+kq+vrxo0aODqUgAAAOBCBCnUOfn5+Zo6dapat24tHx8fNW3aVAMGDNDXX39t3+arr77SoEGDZLVa1ahRI0VHR2vr1q1XHNdisWjp0qU6d+6cLBaLLBaLli1bVqma9uzZo4SEBLVt21YNGzZU8+bNNW7cOJ08edK+zezZs/XEE09Iktq0aWOf48iRI7JYLDp37pzeffdd+/qEhAT7/SwWiw4dOqSEhAQFBgbKarVq7NixOn/+vKnH7sI5+T/88IPuvfde+fv7q0WLFvrLX/4iScrIyNBdd90lPz8/tWrVSu+//77D/U+dOqXp06erc+fO8vf3V0BAgOLi4vTNN9+Um+v1119Xp06d1KhRIwUFBalHjx7lxrtUVlaWwsLCdOutt+rHH380tW8AUNPefPNNderUST4+PgoJCdGkSZMqPMPgb3/7m8LDw+Xr66vrr79eo0aN0tGjR686fnp6uoKDgxUTE6OzZ89WqqasrCw9+uij6tChg3x9fdWkSRMNGzbssp/tKiws1B/+8AcFBwfLz89P9913n44fP15uu3Xr1ikyMlJ+fn5q3Lix7rnnHn377bcO21Sm912wZcsWRUREqGHDhmrXrp3eeuutSu3fpehjcCYvVxcAVLdHHnlEf//73zV58mTdcsstOnnypLZs2aJ9+/ape/fu+uKLLxQXF6fw8HDNmjVLHh4eWrp0qe666y5t3rxZt99+e4XjrlixQosXL9aOHTu0ZMkSSdKdd95ZqZo+//xzZWZmauzYsWrevLm+/fZbLV68WN9++62+/PJLWSwW3X///frPf/6jDz74QK+88oquv/56SVJwcLBWrFihhx9+WLfffrsmTJggSWrXrp3DHMOHD1ebNm2UmJior7/+WkuWLFHTpk01b948U49faWmp4uLiFBUVpRdffFFJSUmaPHmy/Pz8NHPmTD344IO6//77tWjRIo0ePVp33HGH2rRpI0nKzMzURx99pGHDhqlNmzb68ccf9dZbbyk6OlrfffedQkJCJEl//etf9fvf/16/+c1vNGXKFBUUFGjPnj366quvNHLkyArr+v7773XXXXfpuuuu0+eff25/fADAHcyePVtz5sxR//79NXHiRB04cEALFy7Uzp07tXXrVnl7e0v65aIOY8eOVUREhBITE/Xjjz/q//2//6etW7dq9+7dCgwMrHD8nTt3KjY2Vj169NDatWvl6+tbqbp27typbdu2acSIEWrZsqWOHDmihQsXKiYmRt99950aNWrksP1jjz2moKAgzZo1S0eOHNGrr76qyZMna9WqVfZtVqxYoTFjxig2Nlbz5s3T+fPntXDhQvXp00e7d+9W69atJVWu90m/hJuBAwcqODhYs2fPVklJiWbNmqVmzZqZ/Cv8gj4GpzGAOsZqtRqTJk2q8LaysjKjffv2RmxsrFFWVmZff/78eaNNmzbGgAED7OuWLl1qSDIOHz5sXzdmzBjDz8/PdE3nz58vt+6DDz4wJBlpaWn2dfPnzy835wV+fn7GmDFjyq2fNWuWIckYN26cw/r77rvPaNKkiak6x4wZY0gy5s6da193+vRpw9fX17BYLMbKlSvt6/fv329IMmbNmmVfV1BQYJSWljqMefjwYcPHx8d49tln7euGDBlidOrU6Yq1XNiv48ePG/v27TNCQkKMiIgI49SpU6b2CQBqwsU94qeffjIaNGhgDBw40OE18I033jAkGe+8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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n",
      "['[BOS]', '[UNK]', 'does', 'the', '[UNK]', 'say', '?', '[EOS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n",
      "--------------------------------------------------\n",
      "tensor([[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.]], dtype=torch.float64)\n",
      "--------------------------------------------------\n",
      "tensor([[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.]], dtype=torch.float64)\n",
      "--------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1AAAAG1CAYAAAD3DRUpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAR7tJREFUeJzt3XtcVHX+x/E3iAKCM1jhhSBTM80yLbz9VGTE5NJlKfNuq6ibm9qGpW766OKtJCPTLlvKumkXNzOl2s1IvOElLW1T0U1dy0sgWpoK3kAu5/fHPphlAvGAODPA6/l4zGOZM2e+n89hhXcfzswZD8MwDAEAAAAArsjT1Q0AAAAAQHXBAAUAAAAAJjFAAQAAAIBJDFAAAAAAYBIDFAAAAACYxAAFAAAAACYxQAEAAACASQxQAAAAAGASAxQAAAAAmMQABZRh8eLF8vDw0OHDhx22JyYmqkWLFqpTp446dOjgkt5qg2nTpsnDw0MnT550dSsAUMrlMqKqxMXFyd/f/5qsXZbi4/n222+dVvNKinOguiLHajYGKMCk1NRU/fnPf1b37t21aNEizZo1q0rX37Jli6ZNm6YzZ86UemzWrFn69NNPq7QeAAAAKo4BCjBp3bp18vT01N/+9jcNGzZM9957b5Wuv2XLFk2fPp0BCgAAwI0xQAEm/fLLL/L19VW9evVc3QoAAABchAEKNcrZs2c1fvx43XzzzfL29lajRo3Up08ffffdd/Z9vvnmG0VHR8tqtap+/foKDw/XV199Ve66Hh4eWrRokc6fPy8PDw95eHho8eLFpnpKT09XXFycWrRoIR8fHzVp0kQjR47Ur7/+at9n2rRpmjRpkiSpefPm9hqHDx+Wh4eHzp8/r3fffde+PS4uzv48Dw8P/fDDD4qLi1NAQICsVqtGjBihCxcuVOh7V/ya+59++kn333+//P39deONN+ovf/mLJGn37t2KiIiQn5+fmjVrpr///e8Ozz916pQmTpyodu3ayd/fXxaLRTExMdq1a1epWm+88YZuv/121a9fXw0bNlTHjh1LrfdbR44c0S233KI77rhDP//8c4WODQCutbfeeku33367vL29FRQUpHHjxpX5ioKPP/5YoaGh8vX11Q033KBHHnlER48eveL6O3fuVGBgoGw2m86dO2eqpyNHjmjs2LFq3bq1fH19df3116t///6Xfe9WXl6ennrqKQUGBsrPz08PPfSQTpw4UWq/lJQUhYWFyc/PTw0aNNB9992nf//73w77mMm+Yps3b1anTp3k4+Ojli1basGCBaaO77fIMTiLl6sbAKrSY489puXLl+vxxx9X27Zt9euvv2rz5s3au3ev7r77bq1bt04xMTEKDQ3V1KlT5enpqUWLFikiIkKbNm1S586dy1z3/fffV1JSkrZt26aFCxdKkrp162aqp9WrV+vgwYMaMWKEmjRpon//+99KSkrSv//9b3399dfy8PBQ37599Z///Ecffvih5s6dqxtuuEGSFBgYqPfff19/+MMf1LlzZ40ePVqS1LJlS4caAwYMUPPmzZWQkKDvvvtOCxcuVKNGjTR79uwKff8KCwsVExOjnj176uWXX9aSJUv0+OOPy8/PT88884yGDh2qvn37av78+Ro2bJj+7//+T82bN5ckHTx4UJ9++qn69++v5s2b6+eff9aCBQsUHh6u77//XkFBQZKkv/71r3riiSfUr18/xcfHKzc3V+np6frmm280ZMiQMvv68ccfFRERoeuuu06rV6+2f38AwB1MmzZN06dP1z333KMxY8Zo//79evvtt7V9+3Z99dVXqlu3rqT/XqxhxIgR6tSpkxISEvTzzz/rtdde01dffaUdO3YoICCgzPW3b9+uqKgodezYUZ999pl8fX1N9bV9+3Zt2bJFgwYNUnBwsA4fPqy3335bNptN33//verXr++w/5/+9Cc1bNhQU6dO1eHDhzVv3jw9/vjj+uijj+z7vP/++xo+fLiioqI0e/ZsXbhwQW+//bZ69OihHTt26Oabb5ZkLvuk/w41kZGRCgwM1LRp01RQUKCpU6eqcePGFfx/4b/IMTiFAdQgVqvVGDduXJmPFRUVGa1atTKioqKMoqIi+/YLFy4YzZs3N/r06WPftmjRIkOScejQIfu24cOHG35+fhXu6cKFC6W2ffjhh4YkY+PGjfZtiYmJpWoW8/PzM4YPH15q+9SpUw1JxsiRIx22P/TQQ8b1119foT6HDx9uSDJmzZpl33b69GnD19fX8PDwMJYuXWrfvm/fPkOSMXXqVPu23Nxco7Cw0GHNQ4cOGd7e3saMGTPs22JjY43bb7+93F6Kj+vEiRPG3r17jaCgIKNTp07GqVOnKnRMAHAtlMyIX375xahXr54RGRnp8DvwzTff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PfXBBx/owQcf1IABA/TFF1+U+ustALhKYGCgpkyZounTpys6Olq/+93vtH//fr311lvq1KmTHnnkEUlS3bp1NXv2bI0YMULh4eEaPHiw/TLmN998s5588sky1/f19dXnn3+uiIgIxcTEaMOGDbrjjjtM9Xb//ffr/fffl9VqVdu2bbV161atWbNG119/faWO1WKx6O2339bvf/973X333Ro0aJACAwP1008/aeXKlerevbvefPPNCmXf9OnT9eWXXyosLExjx45VQUGB/TOW0tPTK9VnZZFjMM01F/8Dql5eXp4xadIko3379kaDBg0MPz8/o3379sZbb73lsN+OHTuMvn37Gtdff73h7e1tNGvWzBgwYICxdu1a+z5VeRnzzMxM46GHHjICAgIMq9Vq9O/f38jKyip1+VTDMIyZM2caN954o+Hp6elQf9++fUbPnj0NX19fQ5L9kuYlL5NaUln9X8nlji88PLzMy7U2a9bMuO++++z3c3NzjQkTJhhNmzY1fH19je7duxtbt241wsPDjfDwcPt+CxYsMHr27Gn//rds2dKYNGmSkZ2dbd+nrOO6cOGCER4ebvj7+xtff/216eMCgKpW1u/YN99802jTpo1Rt25do3HjxsaYMWOM06dPl3ruRx99ZNx1112Gt7e3cd111xlDhw41MjMzHfYp6/fxyZMnjbZt2xpNmjQxDhw4YKrP06dPGyNGjDBuuOEGw9/f34iKijL27dtnNGvWzOGjMYqPZ/v27Q7PX79+vSHJWL9+fantUVFRhtVqNXx8fIyWLVsacXFxxrfffmvfpyLZt2HDBiM0NNSoV6+e0aJFC2P+/Pn2HKgIcgzO4mEYFXyXOQAAAADUUrwHCgAAAABM4j1QQCVlZ2fr4sWL5e5T1mVena269AkAMOfcuXNlXgyppMDAwMteuru6IcfgbngJH1BJcXFxevfdd8vdxx1+vKpLnwAAc6ZNm1bq84Z+69ChQ7r55pud09A1Ro7B3TBAAZX0/fffKysrq9x9nPV5U+WpLn0CAMw5ePCgDh48WO4+PXr0cPg8ouqMHIO7YYACAAAAAJO4iAQAAAAAmMQABQAAAAAmMUDBbs+ePYqLi3NqzcOHDys1NVWS1LFjR6fWrq2++eYbde/eXXfffbc++OADV7cDAJflilySyCZXIJtQnTBAlZCWlqaQkBAlJSXJ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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "#通过下面代码查看mask的效果\n",
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "inputs_ids, inputs_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "for i in range(len(inputs_ids)):\n",
    "    decode_text = \\\n",
    "        tokenizer.decode(inputs_ids[i:i + 1].tolist(), remove_bos=False, remove_eos=False, remove_pad=False,\n",
    "                         split=True)[0]\n",
    "    print(decode_text)\n",
    "    print(\"-\" * 50)\n",
    "\n",
    "    print(inputs_mask[i].reshape(1, -1))\n",
    "    print(\"-\" * 50)\n",
    "    # reshape(1, -1):将 input_mask[i] 的形状调整为 (1, sequence_length)\n",
    "    # repeat_interleave 在第 0 维（dim=0）上重复 sequence_length 次，生成形状为 (sequence_length, sequence_length) 的掩码。\n",
    "    self_attn_mask = inputs_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)\n",
    "    print(self_attn_mask)\n",
    "    print(\"-\" * 50)\n",
    "\n",
    "    look_ahead_mask = generate_square_subsequent_mask(inputs_ids.shape[-1])\n",
    "\n",
    "    fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axs[0].matshow(self_attn_mask)\n",
    "    axs[0].set_title(\"self_attn_mask\")\n",
    "    axs[0].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_ylabel(\"querys\")\n",
    "    axs[0].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_xlabel(\"keys\")\n",
    "    axs[1].matshow(look_ahead_mask)\n",
    "    axs[1].set_title(\"look_ahead_mask\")\n",
    "    axs[1].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_ylabel(\"querys\")\n",
    "    axs[1].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_xlabel(\"keys\")\n",
    "    plt.show()\n",
    "    print('-' * 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14c5b12ee51329e8",
   "metadata": {},
   "source": [
    "## Transformer Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "89a32ab3ca75bf0c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.215535Z",
     "start_time": "2025-02-06T14:04:31.200067Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.465490Z",
     "iopub.status.busy": "2025-02-07T15:37:44.465157Z",
     "iopub.status.idle": "2025-02-07T15:37:44.484932Z",
     "shell.execute_reply": "2025-02-07T15:37:44.484219Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.465466Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerOutput:\n",
    "    logits: Tensor\n",
    "    encoder_last_hidden_states: Tensor\n",
    "    encoder_attn_scores: List[Tensor]  #画图\n",
    "    decoder_last_hidden_states: Tensor\n",
    "    decoder_self_attn_scores: List[Tensor]  #画图\n",
    "    decoder_cross_attn_scores: List[Tensor]  #画图\n",
    "    preds: Optional[Tensor] = None\n",
    "\n",
    "\n",
    "class TransformerModel(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_encoder_layers = config[\"num_encoder_layers\"]\n",
    "        self.num_decoder_layers = config[\"num_decoder_layers\"]\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        self.bos_idx = config[\"bos_idx\"]\n",
    "        self.eos_idx = config[\"eos_idx\"]\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "        # 是否共享词嵌入，\n",
    "        # 共享后，decoder的词嵌入层和encoder的词嵌入层相同，节省内存\n",
    "        self.share = config[\"share_embedding\"]\n",
    "\n",
    "        # layers\n",
    "        self.src_embedding = TransformerEmbedding(config)  # 输入的嵌入层\n",
    "        # 如果共享词嵌入，则使用src_embedding作为trg_embedding\n",
    "        if self.share:\n",
    "            # 源和目标的嵌入层相同，共享参数，节省内存\n",
    "            self.trg_embedding = self.src_embedding\n",
    "            self.linear = lambda x: torch.matmul(\n",
    "                # x是该层的输入，[batch_size, seq_len, hidden_size]\n",
    "                # self.trg_embedding.get_word_embedding_weight().T: [hidden_size,vocab_size]\n",
    "                x, self.trg_embedding.get_word_embedding_weight().T\n",
    "            )  # 输出层，共享参数，直接拿原有embedding矩阵的转置，节省内存\n",
    "        else:\n",
    "            self.trg_embedding = TransformerEmbedding(config)  # decoder模块的嵌入层\n",
    "            self.linear = nn.Linear(self.hidden_size, self.vocab_size)  # 输出层\n",
    "\n",
    "        self.encoder = TransformerEncoder(config)\n",
    "        self.decoder = TransformerDecoder(config)\n",
    "\n",
    "        self._init_weights()  # init weights\n",
    "\n",
    "    def _init_weights(self):\n",
    "        # self.parameters(): 这个方法返回模型中的所有可学习参数（权重和偏置）。\n",
    "        for p in self.parameters():\n",
    "            if p.dim() > 1:  # 如果参数是二维或更高维（通常是权重矩阵），则执行初始化。\n",
    "                nn.init.xavier_uniform_(p)  # 使用 xavier 均匀分布来初始化权重\n",
    "\n",
    "    def generate_square_subsequent_mask(self, sz: int) -> Tensor:\n",
    "        # 为了生成斜三角的mask\n",
    "        mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "        return mask\n",
    "\n",
    "    # 用于训练,在TransformerBlock中的每一层内是并行的，层间是串行的\n",
    "    def forward(\n",
    "            self, encoder_inputs, decoder_inputs, encoder_inputs_mask=None\n",
    "    ) -> TransformerOutput:\n",
    "        # encoder_inputs: [batch_size, src_len]\n",
    "        # decoder_inputs: [batch_size, trg_len]\n",
    "        # encoder_inputs_mask: [batch_size, src_len]\n",
    "        if encoder_inputs_mask is None:\n",
    "            # 返回一个布尔张量，形状与 encoder_inputs 相同。\n",
    "            # 该布尔张量的每个位置会根据 encoder_inputs 中的值是否等于 self.pad_idx 来设置：\n",
    "            # 如果相等，值为 True（表示该位置是填充位置）。\n",
    "            # 如果不相等，值为 False（表示该位置是有效输入） \n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "            # encoder_inputs_mask: [batch_size, src_len]\n",
    "        # unsqueeze(1): 这个操作在张量的第 1 个维度上增加一个新的维度\n",
    "        #    得到 (batch_size, 1, src_len)。\n",
    "        # unsqueeze(2): 这个操作在张量的第 2 个维度上再次增加一个新的维度。\n",
    "        #    得到 (batch_size, 1, 1, src_len)。\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(2)\n",
    "        # [batch_size, 1, 1, src_len],用于encoder的自注意力\n",
    "\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(decoder_inputs.shape[-1])  # [trg_len, trg_len]\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(decoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len] 用于decoder的自注意力\n",
    "\n",
    "        # 增加decoder_inputs_mask和look_ahead_mask进行组合\n",
    "        decoder_inputs_mask = decoder_inputs.eq(self.pad_idx)\n",
    "        # [batch_size, trg_len]，和上面encoder_inputs_mask一致\n",
    "        decoder_inputs_mask = decoder_inputs_mask.unsqueeze(1).unsqueeze(2)\n",
    "        # [batch_size, 1, 1, trg_len]\n",
    "        decoder_inputs_mask = decoder_inputs_mask + look_ahead_mask\n",
    "        # [batch_size, 1, 1, trg_len]与[1, 1, trg_len, trg_len]相加，得到decoder的自注意力mask\n",
    "        # decoder_inputs_mask : [batch_size, 1, trg_len,trg_len]\n",
    "\n",
    "        # encoding\n",
    "        # src_embedding是TransformerEmbedding(config)\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        # encoder是TransformerEncoder(config)\n",
    "        encoder_outputs = self.encoder(\n",
    "            encoder_inputs_embeds, attn_mask=encoder_inputs_mask\n",
    "        )  # encoder_inputs_mask用于encoder的自注意力,广播去做计算 \n",
    "\n",
    "        # decoding\n",
    "        # decoder_inputs_embeds是TransformerEmbedding(config)\n",
    "        decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "        # decoder是TransformerDecoder(config)\n",
    "        decoder_outputs = self.decoder(\n",
    "            decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "            encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "            attn_mask=decoder_inputs_mask,  # 用于decoder的自注意力,广播去做计算\n",
    "            cross_attn_mask=encoder_inputs_mask,  # 用于decoder的交叉注意力,广播去做计算\n",
    "        )\n",
    "        # decoder_outputs.last_hidden_states: [batch_size, trg_len, hidden_size]\n",
    "        logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "        # [batch_size, trg_len, vocab_size]\n",
    "\n",
    "        return TransformerOutput(\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )\n",
    "\n",
    "    @torch.no_grad()  # 禁用梯度计算\n",
    "    def infer(self, encoder_inputs, encoder_inputs_mask=None) -> TransformerOutput:\n",
    "        # 应对多个样本同时进行推理\n",
    "        if encoder_inputs_mask is None:\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "            # encoder_inputs_mask: [batch_size, src_len]\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(2)\n",
    "        # [batch_size, 1, 1, src_len],用于encoder的自注意力\n",
    "\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(self.max_length)\n",
    "        # [max_length, max_length]\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(encoder_inputs.device)\n",
    "        )  # [1, 1, max_length, max_length] 用于decoder的自注意力\n",
    "\n",
    "        # encoding\n",
    "        # src_embedding是TransformerEmbedding(config)\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        # encoder是TransformerEncoder(config)\n",
    "        # 因为只支持单样本预测，没有paddings，所以不需要mask\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds)\n",
    "        # encoder_outputs.last_hidden_states: [batch_size, src_len, hidden_size]\n",
    "\n",
    "        # decoding,多样本推理\n",
    "        # encoder_inputs.shape[0]: 获取编码器输入的批量大小（batch size），即样本的数量。\n",
    "        # .reshape(-1, 1): 这个操作将张量的形状调整为 (batch_size, 1)，\n",
    "        decoder_inputs = torch.Tensor([self.bos_idx] * encoder_inputs.shape[0]).reshape(-1, 1).long().to(\n",
    "            device=encoder_inputs.device)\n",
    "        # 推理是串行的，每次只推理一个token，所以需要初始化decoder_inputs为bos_idx\n",
    "        for cur_len in tqdm(range(1, self.max_length + 1)):\n",
    "            decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "            decoder_outputs = self.decoder(\n",
    "                decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "                encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "                # decoder的自注意力mask\n",
    "                # 因为是串行的，所以每次只看前面cur_len个token\n",
    "                attn_mask=look_ahead_mask[:, :, :cur_len, :cur_len],\n",
    "            )\n",
    "            # decoder_outputs.last_hidden_states: [batch_size, cur_len, hidden_size]\n",
    "            logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "            # logits: [batch_size, cur_len, vocab_size]\n",
    "            # 通过最大下标确定类别，[:, -1:]表示取最后一个结果\n",
    "            next_token = logits.argmax(dim=-1)[:, -1:]  # [batch_size, 1]\n",
    "            # 预测输出拼接到输入中\n",
    "            decoder_inputs = torch.cat([decoder_inputs, next_token], dim=-1)\n",
    "            # dcoder_inputs: [batch_size, cur_len+1]\n",
    "\n",
    "            # (decoder_inputs == self.eos_idx).sum(dim=-1)是判断样本中是否含有EOS标记\n",
    "            # all是每一个都为True，才会结束\n",
    "            if all((decoder_inputs == self.eos_idx).sum(dim=-1) > 0):\n",
    "                break\n",
    "\n",
    "        return TransformerOutput(\n",
    "            preds=decoder_inputs[:, 1:],  # 去掉bos_idx\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a740def7afa83f9",
   "metadata": {},
   "source": [
    "# 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18175b791877293",
   "metadata": {},
   "source": [
    "## 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "a2b1b4622966eb25",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.222855Z",
     "start_time": "2025-02-06T14:04:31.217548Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.485957Z",
     "iopub.status.busy": "2025-02-07T15:37:44.485633Z",
     "iopub.status.idle": "2025-02-07T15:37:44.490959Z",
     "shell.execute_reply": "2025-02-07T15:37:44.490277Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.485934Z"
    }
   },
   "outputs": [],
   "source": [
    "class CrossEntropyWithPadding:\n",
    "    def __init__(self, config):\n",
    "        self.label_smoothing = config[\"label_smoothing\"]\n",
    "\n",
    "    def __call__(self, logits, labels, padding_mask=None):\n",
    "        # logits: [batch_size, seq_len, vocab_size]\n",
    "        # labels: [batch_size, seq_len]\n",
    "        # padding_mask: [batch_size, seq_len]\n",
    "        bs, seq_len, nc = logits.shape\n",
    "        loss = F.cross_entropy(\n",
    "            logits.reshape(bs * seq_len, nc),\n",
    "            labels.reshape(-1),\n",
    "            reduction='none',  # 不对损失进行归约（reduction\n",
    "            label_smoothing=self.label_smoothing\n",
    "        )  # label_smoothing表示随机将一个类别的概率设置为0.1，使得模型更加关注其他类别\n",
    "        # loss: [batch_size * seq_len, 1]\n",
    "\n",
    "        if padding_mask is None:\n",
    "            loss = loss.mean()  # 计算平均损失\n",
    "        else:\n",
    "            # 将padding_mask reshape成一维张量，mask部分为0，非mask部分为1\n",
    "            padding_mask = 1 - padding_mask.reshape(-1)\n",
    "            loss = torch.mul(loss, padding_mask).sum() / padding_mask.sum()\n",
    "\n",
    "        return loss"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab581173629b9cc",
   "metadata": {},
   "source": [
    "## 学习率衰减"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "39efc556d490f34e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.228130Z",
     "start_time": "2025-02-06T14:04:31.223857Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.492152Z",
     "iopub.status.busy": "2025-02-07T15:37:44.491752Z",
     "iopub.status.idle": "2025-02-07T15:37:44.496537Z",
     "shell.execute_reply": "2025-02-07T15:37:44.496075Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.492125Z"
    }
   },
   "outputs": [],
   "source": [
    "# NoamDecayScheduler 是一个自定义或外部定义的学习率衰减调度器类。\n",
    "# 它需要接收配置 config 作为参数，可能实现了特定的学习率衰减方案\n",
    "class NoamDecayScheduler:\n",
    "    def __init__(self, config):\n",
    "        self.d_model = config[\"d_model\"]\n",
    "        self.warmup_steps = config[\"warmup_steps\"]\n",
    "\n",
    "    # __call__ 方法是一个特殊的方法，用于使对象能够像函数一样被调用\n",
    "    def __call__(self, step):\n",
    "        step += 1\n",
    "        arg1 = step ** (-0.5)  # 4000步之后是arg1\n",
    "        arg2 = step * (self.warmup_steps ** (-1.5))  # 4000步之前是arg2\n",
    "        arg3 = self.d_model ** (-0.5)\n",
    "\n",
    "        return arg3 * np.minimum(arg1, arg2)  # minimum是取两个数中的较小值\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "3188d631aaa09057",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.334627Z",
     "start_time": "2025-02-06T14:04:31.229133Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.497544Z",
     "iopub.status.busy": "2025-02-07T15:37:44.497158Z",
     "iopub.status.idle": "2025-02-07T15:37:44.603032Z",
     "shell.execute_reply": "2025-02-07T15:37:44.602519Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.497523Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "temp_learning_rate_schedule = NoamDecayScheduler({\"d_model\": 512, \"warmup_steps\": 4000})\n",
    "#下面是学习率的设计图\n",
    "plt.plot(temp_learning_rate_schedule(np.arange(0, 40000)))\n",
    "plt.ylabel(\"Leraning rate\")\n",
    "plt.xlabel(\"Train step\")\n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90185dd5b91d52ad",
   "metadata": {},
   "source": [
    "## 优化器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "a5061e4cd6464c1b",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.339984Z",
     "start_time": "2025-02-06T14:04:31.335629Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.603992Z",
     "iopub.status.busy": "2025-02-07T15:37:44.603665Z",
     "iopub.status.idle": "2025-02-07T15:37:44.608163Z",
     "shell.execute_reply": "2025-02-07T15:37:44.607649Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.603960Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.optim.lr_scheduler import LambdaLR\n",
    "from torch.optim import Adam\n",
    "\n",
    "\n",
    "def get_optimizer(model, config):\n",
    "    base_lr = 0.1\n",
    "    beta1 = config[\"beta1\"]  # Adam 的 beta1\n",
    "    beta2 = config[\"beta2\"]  # Adam 的 beta2\n",
    "    eps = config[\"eps\"]\n",
    "    optimizer = Adam(model.parameters(), lr=base_lr, betas=(beta1, beta2), eps=eps)\n",
    "    # config是一个字典，包含了学习率衰减的参数\n",
    "    lr_scheduler = NoamDecayScheduler(config)\n",
    "    # 使用 LambdaLR 调度器，它可以根据给定的函数 lr_lambda 调整学习率。\n",
    "    # 这里将 lr_scheduler 作为函数传递给 LambdaLR，它包含了特定于模型或任务的学习率调度规则\n",
    "    scheduler = LambdaLR(optimizer, lr_lambda=lr_scheduler)\n",
    "    return optimizer, scheduler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "49a2f5433a409089",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.346703Z",
     "start_time": "2025-02-06T14:04:31.340986Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.609066Z",
     "iopub.status.busy": "2025-02-07T15:37:44.608865Z",
     "iopub.status.idle": "2025-02-07T15:37:44.614274Z",
     "shell.execute_reply": "2025-02-07T15:37:44.613804Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.609044Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        self.save_dir = save_dir  # 保存路径\n",
    "        self.save_step = save_step  # 保存步数\n",
    "        self.save_best_only = save_best_only  # 是否只保存最好的模型\n",
    "        self.best_metric = -np.inf  # 最好的指标，指标不可能为负数，所以初始化为-1\n",
    "        # 创建保存路径\n",
    "        if not os.path.exists(self.save_dir):  # 如果不存在保存路径，则创建\n",
    "            os.makedirs(self.save_dir)\n",
    "\n",
    "    # 对象被调用时：当你将对象像函数一样调用时，Python 会自动调用 __call__ 方法。\n",
    "    # state_dict() 返回模型参数的字典，包括模型参数和优化器参数\n",
    "    # metric 是指标，可以是验证集的准确率，也可以是其他指标\n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return  # 不是保存步数，则直接返回\n",
    "\n",
    "        if self.save_best_only:\n",
    "            assert metric is not None  # 必须传入metric\n",
    "            if metric >= self.best_metric:  # 如果当前指标大于最好的指标\n",
    "                # save checkpoint\n",
    "                # 保存最好的模型，覆盖之前的模型，不保存step，只保存state_dict，即模型参数，不保存优化器参数\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"hidden_size_1024.ckpt\"))\n",
    "                self.best_metric = metric  # 更新最好的指标\n",
    "        else:\n",
    "            # 保存模型\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "            # 保存每个step的模型，不覆盖之前的模型，保存step，保存state_dict，即模型参数，不保存优化器参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "1c98380e43240291",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.352748Z",
     "start_time": "2025-02-06T14:04:31.347708Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.614988Z",
     "iopub.status.busy": "2025-02-07T15:37:44.614799Z",
     "iopub.status.idle": "2025-02-07T15:37:44.619151Z",
     "shell.execute_reply": "2025-02-07T15:37:44.618673Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.614959Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        self.patience = patience  # 多少个step没有提升就停止训练\n",
    "        self.min_delta = min_delta  # 最小的提升幅度\n",
    "        self.best_metric = -np.inf  # 记录的最好的指标\n",
    "        self.counter = 0  # 计数器，记录连续多少个step没有提升\n",
    "\n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:  # 如果指标提升了\n",
    "            self.best_metric = metric  # 更新最好的指标\n",
    "            self.counter = 0  # 计数器清零\n",
    "        else:\n",
    "            self.counter += 1  # 计数器加一\n",
    "\n",
    "    @property  # 使用@property装饰器，使得 对象.early_stop可以调用，不需要()\n",
    "    def early_stop(self):\n",
    "        # 如果计数器大于等于patience，则返回True，停止训练\n",
    "        return self.counter >= self.patience"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebb3c8c230d312c1",
   "metadata": {},
   "source": [
    "## training & valuating"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "140f2acfbeb9740a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.358185Z",
     "start_time": "2025-02-06T14:04:31.353752Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.620040Z",
     "iopub.status.busy": "2025-02-07T15:37:44.619694Z",
     "iopub.status.idle": "2025-02-07T15:37:44.624166Z",
     "shell.execute_reply": "2025-02-07T15:37:44.623685Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.620018Z"
    }
   },
   "outputs": [],
   "source": [
    "@torch.no_grad()  # 禁用梯度计算\n",
    "def evaluating(model, data_loader, loss_fct):\n",
    "    loss_list = []\n",
    "    for batch in data_loader:\n",
    "        encoder_inputs = batch[\"encoder_inputs\"]\n",
    "        encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "        decoder_inputs = batch[\"decoder_inputs\"]\n",
    "        decoder_labels = batch[\"decoder_labels\"]\n",
    "        decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "        # 前向计算\n",
    "        outputs = model(\n",
    "            encoder_inputs=encoder_inputs,\n",
    "            decoder_inputs=decoder_inputs,\n",
    "            encoder_inputs_mask=encoder_inputs_mask,\n",
    "        )\n",
    "        logits = outputs.logits\n",
    "        # 计算损失\n",
    "        loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "        loss_list.append(loss.cpu().item())\n",
    "\n",
    "    return np.mean(loss_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "3099e48677dcb6bf",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.366317Z",
     "start_time": "2025-02-06T14:04:31.359188Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.625091Z",
     "iopub.status.busy": "2025-02-07T15:37:44.624851Z",
     "iopub.status.idle": "2025-02-07T15:37:44.633439Z",
     "shell.execute_reply": "2025-02-07T15:37:44.632999Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.625069Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def training(model,\n",
    "             train_loader,\n",
    "             val_loader,\n",
    "             epoch,\n",
    "             loss_fct,\n",
    "             optimizer,\n",
    "             scheduler=None,\n",
    "             save_ckpt_callback=None,\n",
    "             early_stop_callback=None,\n",
    "             eval_step=500,\n",
    "             ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "\n",
    "    global_step = 1  # 全局步数\n",
    "    model.train()  # 训练模式\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # 训练\n",
    "            for batch in train_loader:\n",
    "                encoder_inputs = batch[\"encoder_inputs\"]\n",
    "                encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "                decoder_inputs = batch[\"decoder_inputs\"]\n",
    "                decoder_labels = batch[\"decoder_labels\"]\n",
    "                decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "                optimizer.zero_grad()  # 梯度清零\n",
    "\n",
    "                # 前向传播\n",
    "                outputs = model(\n",
    "                    encoder_inputs=encoder_inputs,\n",
    "                    decoder_inputs=decoder_inputs,\n",
    "                    encoder_inputs_mask=encoder_inputs_mask\n",
    "                )\n",
    "                logits = outputs.logits\n",
    "                loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                if scheduler is not None:\n",
    "                    scheduler.step()  # 更新学习率\n",
    "\n",
    "                loss = loss.cpu().item()\n",
    "\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss,\n",
    "                    \"step\": global_step\n",
    "                })\n",
    "\n",
    "                # 评估\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()  # 评估模式\n",
    "                    # 验证集损失和准确率\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss,\n",
    "                        \"step\": global_step\n",
    "                    })\n",
    "                    model.train()  # 训练模式\n",
    "\n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        # model.state_dict() 返回模型参数的字典，包括模型参数和优化器参数\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), -val_loss)\n",
    "                        # 保存最好的模型，覆盖之前的模型，保存step，保存state_dict,通过metric判断是否保存最好的模型\n",
    "\n",
    "                    # 3. 早停 early stopping\n",
    "                    if early_stop_callback is not None:\n",
    "                        # 验证集准确率不再提升，则停止训练\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        # 验证集准确率不再提升，则停止训练\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict  # 早停，返回记录字典 record_dict\n",
    "\n",
    "                # 更新进度条和全局步数\n",
    "                pbar.update(1)  # 更新进度条\n",
    "                global_step += 1  # 全局步数加一\n",
    "            pbar.set_postfix({\"epoch\": epoch_id, \"loss\": loss, \"val_loss\": val_loss})\n",
    "\n",
    "    return record_dict  # 训练结束，返回记录字典 record_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "cf04ac5e18afea4f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:31.441762Z",
     "start_time": "2025-02-06T14:04:31.367320Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.634284Z",
     "iopub.status.busy": "2025-02-07T15:37:44.633987Z",
     "iopub.status.idle": "2025-02-07T15:37:44.828197Z",
     "shell.execute_reply": "2025-02-07T15:37:44.827641Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.634264Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16/.cache/de2en_train_128.npy\n",
      "load val dataset from wmt16/.cache/de2en_val_128.npy\n"
     ]
    }
   ],
   "source": [
    "#模型的超参\n",
    "config = {\n",
    "    \"bos_idx\": 1,\n",
    "    \"eos_idx\": 3,\n",
    "    \"pad_idx\": 0,\n",
    "    \"vocab_size\": len(word2idx),\n",
    "    \"max_length\": 128,\n",
    "    \"d_model\": 1024, # 可调整\n",
    "    \"dim_feedforward\": 2048,  # FFN 的隐藏层大小\n",
    "    \"dropout\": 0.1, # 可调整\n",
    "    \"layer_norm_eps\": 1e-6,  # 层归一化的 epsilon, 防止除零错误\n",
    "    \"num_heads\": 8, # 可调整\n",
    "    \"num_decoder_layers\": 6, # 可调整\n",
    "    \"num_encoder_layers\": 6, # 可调整\n",
    "    \"label_smoothing\": 0.1,\n",
    "    \"beta1\": 0.9,  # Adam 的 beta1\n",
    "    \"beta2\": 0.98,  # Adam 的 beta2\n",
    "    \"eps\": 1e-9,\n",
    "    \"warmup_steps\": 4_000,\n",
    "    \"share_embedding\": False,  # 是否共享词向量\n",
    "}\n",
    "\n",
    "\n",
    "def get_dl(dataset, batch_size, shuffle=True):\n",
    "    sampler = TransformerBatchSampler(dataset, batch_size=batch_size, shuffle_batch=shuffle)\n",
    "    sample_dl = DataLoader(dataset, batch_sampler=sampler, collate_fn=partial(collate_fn, tokenizer=tokenizer))\n",
    "    return sample_dl\n",
    "\n",
    "\n",
    "# dataset\n",
    "train_ds = LangPairDataset(\"train\", max_length=config[\"max_length\"])\n",
    "val_ds = LangPairDataset(\"val\", max_length=config[\"max_length\"])\n",
    "\n",
    "# tokenizer\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word, max_length=config[\"max_length\"])\n",
    "\n",
    "# dataloader\n",
    "batch_size = 2048\n",
    "train_dl = get_dl(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_dl = get_dl(val_ds, batch_size=batch_size, shuffle=False)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "c4bd1cb45f5032ee",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:32.221751Z",
     "start_time": "2025-02-06T14:04:31.442764Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:44.829060Z",
     "iopub.status.busy": "2025-02-07T15:37:44.828796Z",
     "iopub.status.idle": "2025-02-07T15:37:47.259207Z",
     "shell.execute_reply": "2025-02-07T15:37:47.258650Z",
     "shell.execute_reply.started": "2025-02-07T15:37:44.829038Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型参数量: 181582527\n"
     ]
    }
   ],
   "source": [
    "#计算模型参数量\n",
    "model = TransformerModel(config)\n",
    "print(f\"模型参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "6b87e4917b9ef89b",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T14:04:53.810386Z",
     "start_time": "2025-02-06T14:04:32.224757Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-07T15:37:47.260365Z",
     "iopub.status.busy": "2025-02-07T15:37:47.259924Z",
     "iopub.status.idle": "2025-02-07T16:13:51.029903Z",
     "shell.execute_reply": "2025-02-07T16:13:51.029309Z",
     "shell.execute_reply.started": "2025-02-07T15:37:47.260342Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "27499it [36:00, 12.73it/s, epoch=25, loss=3.04, val_loss=3.5]                         "
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 26 / global_step 27500\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "epoch = 100\n",
    "\n",
    "# model\n",
    "model = TransformerModel(config)\n",
    "model = model.to(device)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "\n",
    "# 2. 定义优化器\n",
    "optimizer, scheduler = get_optimizer(model, config)\n",
    "\n",
    "# 3.save model checkpoint\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(save_dir=\"checkpoints\", save_step=500, save_best_only=True)\n",
    "\n",
    "# 4. early stopping\n",
    "early_stop_callback = EarlyStopCallback(patience=10,min_delta=0.001)\n",
    "\n",
    "record_dict = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    val_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=500\n",
    ")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50ac7e66d5df05c3",
   "metadata": {},
   "source": [
    "# 推理\n",
    "\n",
    "- 翻译项目的评估指标一般是BLEU4\n",
    "- 接下来进行翻译推理，并作出注意力的热度图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "305c0410623a9f8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-07T16:13:51.031550Z",
     "iopub.status.busy": "2025-02-07T16:13:51.031159Z",
     "iopub.status.idle": "2025-02-07T16:13:51.392163Z",
     "shell.execute_reply": "2025-02-07T16:13:51.391606Z",
     "shell.execute_reply.started": "2025-02-07T16:13:51.031523Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "# 加载模型\n",
    "state_dict = torch.load(f\"checkpoints/hidden_size_1024.ckpt\", map_location=\"cpu\",weights_only=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "6f512e7c8f2db972",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-07T16:13:51.393031Z",
     "iopub.status.busy": "2025-02-07T16:13:51.392827Z",
     "iopub.status.idle": "2025-02-07T16:14:05.945675Z",
     "shell.execute_reply": "2025-02-07T16:14:05.945066Z",
     "shell.execute_reply.started": "2025-02-07T16:13:51.393009Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load test dataset from wmt16/.cache/de2en_test_128.npy\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0it [00:00, ?it/s]/usr/local/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n",
      "The hypothesis contains 0 counts of 4-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n",
      "8it [00:00, 75.98it/s]/usr/local/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n",
      "The hypothesis contains 0 counts of 3-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n",
      "26it [00:00, 83.57it/s]/usr/local/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n",
      "The hypothesis contains 0 counts of 2-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n",
      "966it [00:11, 82.33it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "testing loss: 3.343610713570755\n",
      "testing bleu: 0.5860467893203264\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "from nltk.translate.bleu_score import sentence_bleu\n",
    "\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "\n",
    "test_ds = LangPairDataset(\"test\", max_length=config[\"max_length\"])\n",
    "test_dl = DataLoader(\n",
    "    test_ds, batch_size=1,\n",
    "    collate_fn=partial(collate_fn, tokenizer=tokenizer)\n",
    ")\n",
    "\n",
    "model = model.to(device)\n",
    "model.eval()\n",
    "collect = {}\n",
    "loss_collect = []\n",
    "\n",
    "predictions = []\n",
    "answers = []\n",
    "\n",
    "# 初始化BLEU分数列表\n",
    "bleu_scores = []\n",
    "for idx, batch in tqdm(enumerate(test_dl)):\n",
    "    encoder_inputs = batch[\"encoder_inputs\"]\n",
    "    encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "    decoder_inputs = batch[\"decoder_inputs\"]\n",
    "    decoder_labels = batch[\"decoder_labels\"]\n",
    "\n",
    "    # 前向计算\n",
    "    outputs = model(\n",
    "        encoder_inputs=encoder_inputs,\n",
    "        decoder_inputs=decoder_inputs,\n",
    "        encoder_inputs_mask=encoder_inputs_mask\n",
    "    )\n",
    "\n",
    "    loss = loss_fct(outputs.logits, decoder_labels)  # 验证集损失\n",
    "\n",
    "    preds = outputs.logits.argmax(dim=-1)  # 预测结果，[1,seq_len]\n",
    "    # 把preds转为英文单词\n",
    "    preds = tokenizer.decode(preds.cpu().numpy())  #['预测句子']\n",
    "    #把decoder_labels转为英文单词\n",
    "    decoder_labels = tokenizer.decode(decoder_labels.cpu().numpy())  #['标签句子']\n",
    "\n",
    "    answers.append(decoder_labels)\n",
    "\n",
    "    belu = sentence_bleu([decoder_labels[0].split()], preds[0].split(), weights=(1, 0, 0, 0))\n",
    "    bleu_scores.append(belu)\n",
    "    collect[idx] = {\n",
    "        \"loss\": loss.item(),\n",
    "        \"src_inputs\": encoder_inputs,\n",
    "        \"trg_inputs\": decoder_inputs,\n",
    "        \"mask\": encoder_inputs_mask,\n",
    "        \"trg_labels\": decoder_labels,\n",
    "        \"preds\": preds\n",
    "    }\n",
    "    loss_collect.append(loss.item())\n",
    "\n",
    "# sort collect by value\n",
    "collect = sorted(collect.items(), key=lambda x: x[1][\"loss\"])\n",
    "print(f\"testing loss: {np.array(loss_collect).mean()}\")\n",
    "belu_scores = sum(bleu_scores) / len(bleu_scores)\n",
    "print(f\"testing bleu: {belu_scores}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "a9ab2039d24b72a3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-07T16:14:05.946721Z",
     "iopub.status.busy": "2025-02-07T16:14:05.946364Z",
     "iopub.status.idle": "2025-02-07T16:14:05.949223Z",
     "shell.execute_reply": "2025-02-07T16:14:05.948735Z",
     "shell.execute_reply.started": "2025-02-07T16:14:05.946687Z"
    }
   },
   "outputs": [],
   "source": [
    "# !pip install Cython  # if failed to install fastBPE, try this line\n",
    "# !pip install fastBPE -v\n",
    "# 在 Windows 系统上并没有 sys/mman.h 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "1e7c1e0999eb365d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-07T16:14:05.950216Z",
     "iopub.status.busy": "2025-02-07T16:14:05.949848Z",
     "iopub.status.idle": "2025-02-07T16:14:06.350662Z",
     "shell.execute_reply": "2025-02-07T16:14:06.350128Z",
     "shell.execute_reply.started": "2025-02-07T16:14:05.950194Z"
    }
   },
   "outputs": [],
   "source": [
    "import re\n",
    "from fastBPE import fastBPE\n",
    "from sacremoses import MosesDetokenizer, MosesTokenizer\n",
    "\n",
    "# `MosesTokenizer` 和 `MosesDetokenizer` 是来自 `sacremoses` 库的工具，用于自然语言处理中的分词（Tokenization）和去标记化（Detokenization）。\n",
    "# 这些工具主要用于对文本进行预处理和后处理，通常在处理自然语言处理任务时会用到。\n",
    "\n",
    "# 这些工具通常在文本预处理和后处理过程中使用，对输入的文本进行标记化和去标记化，是一种常用的处理方式。\n",
    "# 在自然语言处理任务中，对文本进行正确的分词和还原是很重要的，而 `MosesTokenizer` 和 `MosesDetokenizer` 提供了方便、高效的工具来处理这些任务。\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.bpe = fastBPE(\"./wmt16/bpe.20000\", \"./wmt16/vocab\")\n",
    "        self.mose_tokenizer = MosesTokenizer(lang=\"de\")\n",
    "        self.mose_detokenizer = MosesDetokenizer(lang=\"en\")\n",
    "        self.model = model\n",
    "        self.model.eval()\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "        # 匹配出现的 @@ （后面带空格的情况）。\n",
    "        # 或者匹配以 @@ 结尾的情况（可选空格）。\n",
    "        self.pattern = re.compile(r'(@@ )|(@@ ?$)')\n",
    "        \n",
    "    def draw_attention_map(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "        attn_scores (numpy.ndarray): 表示自注意力机制（self-attention）分数。\n",
    "        cross_attn_scores (numpy.ndarray): 表示交叉注意力机制的注意力分数。\n",
    "        src_words_list (list): 源语言句子的单词列表。\n",
    "        trg_words_list (list): 目标语言句子的单词列表。\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "\n",
    "        fig = plt.figure(figsize=(10, 5), constrained_layout=True) # constrained_layout=True 自动调整子图参数，使之填充整个图像区域\n",
    "        grid = plt.GridSpec(trg_len, trg_len + src_len, wspace=0.1, hspace=0.1)# wspace,hspace 控制子图之间的间距\n",
    "        #下面是attn_scores的热力图\n",
    "        self_map = fig.add_subplot(grid[:,:trg_len]) #  添加子图\n",
    "        self_map.matshow(attn_scores.mean(dim=0), cmap='viridis') # 绘制热力图，cmap表示颜色,dim=0表示对第0维求均值\n",
    "        self_map.set_yticks(range(trg_len), trg_words_list, fontsize=10)\n",
    "        self_map.set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "        #下面是cross_attn_scores的热力图\n",
    "        cross_map = fig.add_subplot(grid[:, trg_len:])\n",
    "        cross_map.matshow(cross_attn_scores.mean(dim=0), cmap='viridis')\n",
    "        cross_map.set_yticks(range(trg_len), [], fontsize=6)\n",
    "        cross_map.set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    def draw_attention_maps(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list, heads_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "\n",
    "        Args:\n",
    "            - scores (numpy.ndarray): shape = [source sequence length, target sequence length]\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "        # cross_attn_scores = cross_attn_scores[:, :len(src_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "        fig, axes = plt.subplots(2, len(heads_list), figsize=(5 * len(heads_list), 10))\n",
    "        for i, heads_idx in enumerate(heads_list):\n",
    "            axes[0, i].matshow(attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[0, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[0, i].set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "            axes[0, i].set_title(f\"head {heads_idx}\")\n",
    "            axes[1, i].matshow(cross_attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[1, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[1, i].set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "            axes[1, i].set_title(f\"head {heads_idx}\")\n",
    "\n",
    "        plt.show()\n",
    "    \n",
    "    def __call__(self, sentence_list,heads_list=None,layer_idx=-1):\n",
    "        # 将输入句子列表转换为小写，并使用 MosesTokenizer 进行分词处理。\n",
    "        sentence_list = [\" \".join(self.mose_tokenizer.tokenize(s.lower())) for s in sentence_list]\n",
    "        # 将分词后的结果进行 BPE 编码，得到 tokens_list。\n",
    "        tokens_list = [s.split() for s in self.bpe.apply(sentence_list)]\n",
    "        \n",
    "        # 使用 src_tokenizer 对 tokens_list 进行编码，同时添加起始标记 ([BOS]) 和结束标记 ([EOS])。\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            tokens_list,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            )\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64)\n",
    "        \n",
    "        # 使用模型的 infer 方法对编码器输入进行推理，得到输出结果 outputs\n",
    "        outputs = model.infer(encoder_inputs=encoder_input, encoder_inputs_mask=attn_mask)\n",
    "        preds = outputs.preds.numpy() # 推理结果\n",
    "        \n",
    "        # 使用目标语言的 trg_tokenizer 对预测序列进行解码，得到解码后的目标语言句子列表 trg_decoded。\n",
    "        trg_decoded = self.trg_tokenizer.decode(preds, split=True, remove_eos=False, remove_bos=False, remove_pad=False)\n",
    "        # 使用源语言的 src_tokenizer 对编码器输入进行解码，得到解码后的源语言句子列表 src_decoded。为下面绘制热力图做准备。\n",
    "        src_decoded = self.src_tokenizer.decode(\n",
    "            encoder_input.numpy(),\n",
    "            split=True,\n",
    "            remove_bos=False,\n",
    "            remove_eos=False\n",
    "            )\n",
    "        \n",
    "         # draw the attention map of the last decoder block\n",
    "        for attn_score, cross_attn_score, src, trg in zip(\n",
    "            outputs.decoder_self_attn_scores[layer_idx], outputs.decoder_cross_attn_scores[layer_idx], src_decoded, trg_decoded):\n",
    "            if heads_list is None:# 如果没有指定heads_list，就画单个热力图\n",
    "                self.draw_attention_map(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                )\n",
    "            else:# 如果指定了heads_list，就画多个热力图\n",
    "                self.draw_attention_maps(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                    heads_list=heads_list,\n",
    "                    )\n",
    "        \n",
    "        #将解码后的目标语言句子列表返回，并使用 mose_detokenizer 进行去标记化，最终得到翻译后的结果。\n",
    "        return [self.mose_detokenizer.tokenize(self.pattern.sub(\"\", s).split()) for s in self.trg_tokenizer.decode(preds)] \n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "c5d288cf83d67b62",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-07T16:14:06.354681Z",
     "iopub.status.busy": "2025-02-07T16:14:06.354236Z",
     "iopub.status.idle": "2025-02-07T16:14:09.481689Z",
     "shell.execute_reply": "2025-02-07T16:14:09.481090Z",
     "shell.execute_reply.started": "2025-02-07T16:14:06.354658Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading vocabulary from ./wmt16/vocab ...\n",
      "Read 762259 words (18107 unique) from vocabulary file.\n",
      "Loading codes from ./wmt16/bpe.20000 ...\n",
      "Read 20001 codes from the codes file.\n",
      " 10%|█         | 13/128 [00:00<00:02, 42.50it/s]\n",
      "/usr/local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: There are no gridspecs with layoutgrids. Possibly did not call parent GridSpec with the \"figure\" keyword\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAy8AAAHVCAYAAAD4u8RmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAWOJJREFUeJzt3Xt8j/X/x/HnZ6fPxo7mNMz5OIakpJKFDqQc+qqQUvh+dVBI30xRIvOrHKa+9Q1F9at0Pnw7oDBClHJIkzNbpTBsbMy2z/v3R1+fnxlyuD7XZ9c87rfbdWPX59r1fF/btev9eX2u63pfLmOMEQAAAACUcgH+bgAAAAAAnAmKFwAAAACOQPECAAAAwBEoXgAAAAA4AsULAAAAAEegeAEAAADgCBQvAAAAAByB4gUAAACAI1C8AAAAAHAEihcAAAAAjkDxAgAAAMARKF4AAAAAOEKQvxsAAADgdB6PR1u2bNHu3bvl8XiKvXbVVVdZmrVgwQItWLDgpFmvvPKKpVlAaUPxAgAAcB5WrFihPn36aOfOnTLGFHvN5XKpqKjIsqyxY8fqySefVOvWrRUXFyeXy2XZugEncJkT/8oAAABwxlq2bKmGDRtq7NixJy0ooqKiLMuKi4vT008/rX79+lm2TsBJKF4AAADOQ/ny5bV27VrVr1/f51mxsbH69ttvVa9ePZ9nAaURN+wDAACchzZt2mjLli22ZA0cOFBvvvmmLVlAacQ9LwAAAOdhyJAheuihh/T7778rMTFRwcHBxV5v3ry5ZVlHjhzR9OnT9dVXX6l58+YlsiZPnmxZFlAacdkYAADAeQgIKHkhi8vlkjHG8hv2r7766lO+5nK5tHDhQsuygNKI4gUAAJQpWVlZGjNmjBYtWnTS4YT37dtnad7OnTtP+3qtWrUszYM17N5PYA0uGwMAAGVKv379tGXLFg0YMEBVqlTx+XDC/ihOtmzZoq1bt+qqq65SWFiY9ywPzpzd+wmswZkXAABQpkRERGjp0qVq0aKFbZmvv/66/v3vf2v79u365ptvVKtWLU2dOlV16tRRt27dLMvJysrSLbfcokWLFsnlcmnz5s2qW7eu7r77bsXExGjSpEmWZZV1/thPcP4YbQwAAJQpjRs31uHDh23Le/HFFzV8+HB16dJFBw4c8N7jEh0dralTp1qaNWzYMAUHBysjI0PlypXzzr/11ls1d+5cS7PKOrv3E1iDMy//1apVq7Na3uVy6ZNPPlH16tV91CKcC06jA3Aif/ZBmzdvPuU1/2PGjDnv9fvDd999p5EjR2rMmDFq1qxZiRG5IiMjLc1LSEjQhAkT1L17d0VERGjt2rWqW7eu1q9fr6SkJO3du9eyrKpVq2revHlq0aJFsaxt27apefPmOnTokGVZZZ3d+0lZs27durP+noSEBAUFnd9dK9zz8l9r1qzRQw89pPDw8L9c1hijiRMnKj8/34aW4UxkZWXp1ltv1cKFC4udRh8wYACn0QGUev7qg2bMmKF77rlHFStWVNWqVYt92ONyuRxbvERHRysnJ0cdOnQoNt8Xo39J0vbt23XRRReVmO92u5Wbm2tpVm5ubrEzLsfs27dPbrfb0qyyzu79pKxp2bKld1S9MxEQEKBNmzapbt2655VL8XKchx9+WJUrVz6jZXkzXLoMGzZMQUFBysjIUJMmTbzzb731Vg0fPpzfVynTs2fPM172gw8+8GFLgNLDH33Q+PHj9dRTT+mRRx6xZH2lRd++fRUcHKw333zTlhux69SpozVr1pS4cX/u3LnF+iQrtGvXTq+99prGjRsn6c8i0+Px6Omnnz7tMMooye79pCxauXKlKlWq9JfLGWPUrFkzSzIpXv5r+/btZ/TDPyY9PV3VqlXzYYtwNubPn6958+apRo0axeY3aNDgL4ewhP2ioqL83QSgVPFXH7R//3716tXrvNdT2qxfv16rV69Wo0aNbMkbPny47rvvPh05ckTGGH377bd66623lJKSopkzZ1qa9fTTT6tjx45atWqVjh49qn/+85/66aeftG/fPi1btszSrLLO7v2krGnfvr3q16+v6OjoM1r+2CX954vi5b/OdpjD+Ph4H7UE54LT6M4ya9YsfzcBKFX81Qf16tVL8+fP1+DBgy1ZX2nRunVrZWZm2vamdODAgQoLC9Njjz2mvLw89enTR9WqVVNqaqpuu+02S7OaNWumTZs26fnnn1dERIQOHTqknj176r777lNcXJylWWWd3ftJWbNo0aKzWv7zzz+3JJcb9o+zd+9e5ebmFutEfvrpJz377LPKzc1V9+7d1adPHz+2EKfSpUsXXXzxxRo3bpwiIiK0bt061apVS7fddps8Ho/ee+89fzcRAE7LH31QSkqKJk+erBtuuEGJiYklblh+4IEHLM2zy7vvvqsnnnhCDz/88Em3q3nz5j7LzsvL06FDh874EkD4jz/3k7KusLBQR44cOaP7+M4WxctxevfurWrVqnmvJd69e7caN26satWqqV69evriiy/08ssvq1+/fn5uKU60fv16dezYUa1atdLChQt10003FTuNXq9ePX83Ece56KKLzvja4h9++MHHrQFKB3/0QXXq1Dnlay6XS9u2bbMsy04BASWfBHHsxuKydCN2UVGRhg8frgULFuiiiy7S5MmTz+rywwvdhbKf+NJ//vMfZWVlqX///t55Tz31lMaNG6fCwkJ16NBBb7/9tmJiYizL5LKx46xYsUKzZ8/2fv3aa6+pQoUKWrNmjYKCgvTss8/qX//6F8VLKWT3afS7775bqampioiIKDY/NzdXQ4YM0SuvvGJ5ZlnSvXt3v2VnZGQoPj6+RPFkjFFmZqZq1qzpp5bhQuePPmj79u2Wras0sXu7/vjjD40YMUILFizQ7t27S4y+dL5vgg8cOKCHHnpI8+bN0+zZs9WpUydJ0oMPPqgPP/xQgwYN0ty5czVkyBDNmTPnvLIuJGV1/7fT5MmT9be//c379fLlyzVmzBg9+eSTatKkiR599FGNGzdOkydPtiyTMy/HCQsL088//+w9Zd+lSxc1a9ZMTz/9tCRp06ZNatu2rbKysvzZTJzEqd6QHnvN6jekgYGB2rVrV4nLAvbu3auqVauqsLDQ0jxY51S/u6ysLFWuXJlP2uA39EHO1blzZ2VkZOj+++9XXFxcib6oW7du57X+vn37KisrS+3atVN6erreeOMNffDBB7rtttu0dOlSXXrppfrxxx/VoUMH7dmz57yyTsQxE6dTuXJlzZs3zztU+PDhw5Wenu59YOrnn3+uBx98UJs3b7YskzMvx4mMjNSBAwe8Hce3336rAQMGeF93uVw+ebZLUVGRZs+e7f3E5sSHhC1cuNDyzLKmTp06pzy41qlTx7KDa05OjowxMsbo4MGDCg0N9b5WVFSkzz//nOucS7lTPbj00KFDxX6fgN380QeV5f7n9ddf17///W9t375d33zzjWrVqqWpU6eqTp06511MnGjp0qX6+uuv1bJlS0vXe8zcuXO1fPly1alTR5dddpliYmKUk5Oj0aNH69JLL5UklStXzidPiz/VZ9z5+fkKCQmxPM9udu4nZdHBgwcVGxvr/Xrp0qXFRjBs2rSpfvvtN0szKV6Oc9lll2natGmaMWOGPvjgAx08eLDYg4s2bdrkk1HGHnzwQc2ePVs33HCDmjVrxjjj58CuN6TR0dFyuVxyuVxq2LBhidddLpfGjh1rWd6FoKioSFOmTNE777yjjIwMHT16tNjr+/btsyRn+PDhkv78HY0ePbrY6HRFRUVauXKlz954AGfCH31QWe1/XnzxRY0ZM0ZDhw7VU0895f0AKzo6WlOnTrX8TWl8fPwZP6jvXISEhOjgwYMKCQnR8uXLNX/+fFWoUEFXXnmld5k1a9aobdu2lmVOmzZN0p/HzJkzZxa78bqoqEhLlixR48aNLcvzB7v3k7KoevXq2rBhg2rWrKlDhw5p7dq1mjJlivf1rKysk44Gez64bOw469atU8eOHZWTk6PCwkKNGjXK+xAoSerXr5/Kly+vf//735bmVqxYUa+99pq6dOli6XovBMfekKampmrQoEEnfUMaGBho2dj3ixcvljFGHTp00Pvvv68KFSp4XwsJCVGtWrV4/s9ZGjNmjGbOnKmHHnpIjz32mB599FHt2LFDH330kcaMGWPZaEfHHt62ePFitW3bttgnhiEhIapdu7ZGjBihBg0aWJJ3IeATS2v5ow8qq/1PQkKCJkyYoO7duysiIkJr165V3bp1tX79eiUlJWnv3r2W5s2fP1+TJk3SSy+9pNq1a1u6bknq37+/Vq1apREjRhTrd07mpptusiTz2GAOO3fuVI0aNRQYGOh97dgx88knn1SbNm0syfMHu/eTsig5OVkfffSRRo0apc8//1zLly/Xtm3bvPvL9OnT9dprr2np0qXWhRoUs2fPHvPRRx+ZFStWlHjt008/Ndu2bbM8My4uzmzcuNHy9V4IkpKSTFJSknG5XObyyy/3fp2UlGSuvfZa8/e//91s2rTJ0syCggLTv39/k5GRYel6L1R169Y1n376qTHGmPDwcLNlyxZjjDGpqammd+/eluf179/fZGdnW77eC80LL7xgKlasaMaPH2/CwsLM1q1bjTHGzJo1yyQlJfm5dc5ldx9UVvuf0NBQs2PHDmPMn8eVY/vnpk2bTGhoqOV50dHRJiQkxAQEBJjw8HATExNTbDpf+/btM3369DGxsbHG5XKdcgoICLBga4pLSkoy+/bts3y9pYHd+0lZlJeXZ/r162eio6NN48aNzZIlS4q9npSUZCZOnGhpJmdeSoFJkyZp27Ztev7558vMKXu73XXXXUpNTVVkZKQteREREfrxxx998gnbhaZ8+fLeU85xcXH67LPP1KpVK23btk0XXXSRsrOz/d1EnASfWJYNZbX/SUhIUEpKirp161Zs/3zuuec0a9Ysy4dgf/XVV0/7+p133mlpnr8ce8tYVvYVu/cTWIN7Xk5QWFioKVOm6K233tKmTZskSQ0bNlSfPn304IMPlniAkRWWLl2qRYsW6YsvvlDTpk1LZHzwwQeWZ5Y1dj+xvUOHDlq8eDHFiwVq1KihXbt2qWbNmqpXr57mz5+vVq1a6bvvvpPb7fZJ5qpVq055jw1/b2dm+/bt3tFljud2u5Wbm+uHFpUNdvdBZbX/GT58uO677z4dOXJExhh9++23euutt5SSkqKZM2danldWipNTee211/TMM894R4xq2LChHn74Ycc/OsLu/aSwsFBpaWnaunWr+vTpo4iICP3222+KjIz0ycMc7bZu3bpixy1fPeST4uU4hw8f1jXXXKNvvvlGnTp10lVXXSVJ2rBhgx555BF98sknmj9/vuUjEkVHR6tHjx6WrvNC0LNnT82ePVuRkZHq2bPnaZe1ugPu3LmzRo4cqR9//FEXX3yxypcvX+x1q645vhD06NFDCxYsUJs2bTRkyBDdfvvtevnll5WRkaFhw4ZZnjdnzhzdcccduu666zR//nxde+212rRpk/744w/+Ds9CnTp1tGbNmmJPg5f+HBWpSZMmfmqVs/mjDyqr/c/AgQMVFhamxx57THl5eerTp4+qVaum1NRU3XbbbZZk5OTkeM/25+TknHbZ870qYNq0afr73/+u0NBQ7430p2LVfYLHTJ48WaNHj9b999+vK664QtKfRe/gwYO1d+9eS47Tw4cP17hx41S+fHktWbJEl19+uYKCfP8W1Y795JidO3fq+uuvV0ZGhvLz83XNNdcoIiJC//M//6P8/HzL76e207GREdPT04udnWvatKlefvllXXLJJdYGWnoRmsONGTPG1KxZ06xdu7bEa2vWrDE1a9Y0jz/+uP0Nw0n179/f5OTkeP9/uslqdl9zfCFZvny5mTRpkvnkk098sv7ExETz/PPPG2P+/xpnj8djBg0aZMaMGeOTzLJoxowZpnr16mbOnDmmfPny5q233jLjx4/3/h9njz7IN3Jzc80ff/xh+XoDAgK86z127D9xsqpPqF27ttm7d6/3/6ea6tSpc95ZJ8t+9dVXS8yfPXu2qV27tiUZQUFB5vfffzfGFP+52slX+8kx3bp1M7fffrvJz88vdn/NokWLTP369X2W62s//fSTCQ8PN5dccol58803zerVq83q1avNG2+8YVq3bm0iIiLMTz/9ZGkm97wcp1GjRpowYYJuvvnmk77+7rvv6tFHH/WeEgPgPOXLl9dPP/2k2rVrKzY2VmlpaUpMTNSGDRvUoUMH7dq1y99NdIw33nhDTzzxhLZu3SpJqlatmsaOHVvs2SQ4c/RBzrJ48WJdccUVCgoK0uLFi0+7bPv27W1qlfVCQ0O1fv161a9fv9j8zZs3KzExUUeOHDnvjAYNGuiWW27Rtddeq6uvvloffvihYmJiTrrssTOSThMbG6vly5erUaNGxe6v2bFjhxISEpSXl+fvJp6TW265RYWFhXr//fdL3AtljFHPnj0VHBysd955x7JMLhs7zs6dO70PezqZyy67TBkZGT7Jfu+99055DT43jKGs++2337R06dKTPiTP6ksgYmJidPDgQUl/jk+/fv16JSYm6sCBA47tPPwhJydHffv2Vd++fZWXl6dDhw55H9C6ZcuWEm908Nf81QeVxf7njz/+0IgRI7wP3zzxc1orHlx8fEHij+Lk6NGj2r59u+rVq+fTS6zq16+vd955R6NGjSo2/+2337ZsaPlnnnlGgwcPVkpKilwu1ykvZXS5XJY9dFqyZz85xuPxnHR9v/zyiyIiIizLsduxe+ZONoiDy+XSqFGjLB+KneLlOJGRkdq9e/cpHwL2+++/+2QHmzZtmh599FH1799fH3/8se666y5t3bpV3333ne677z7L88oiOw5A/rzmuCybPXu2/vGPfygkJESxsbHFDoAul8vyn+VVV12lL7/8UomJierVq5cefPBBLVy4UF9++aU6duxoaVZZdsMNN+irr76S2+1WuXLlvM9Y2rhxozp27KhffvnFzy10Hn/0QWW1/+nfv78yMjI0evRoxcXF2TI61tdff62XXnpJ27Zt07vvvqvq1avr9ddfV506dYo9TPJ85eXlaciQId4RzjZt2qS6detqyJAhql69ukaOHGlZliSNHTtWt956q5YsWeK952XZsmVasGCBZZ+md+/eXd27d9ehQ4cUGRmp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oKDYiAghISHF3kgFBQX5pEP+29/+prS0NFs6j/Hjx+upp57SI4884vOsY9LT00/6yawvPrGJiYlRbGysYmJiFB0draCgIFWqVMmSdZ94gO7Ro4cl6z0T7du314EDB/Tyyy97h6pMSEjQgAEDLO84jrHr9xYdHW3p6DFnkvfxxx9ry5Yt3hGAEhISVL9+fcuzyvLf29ChQ3XgwAGtXLlSSUlJ+vDDD/XHH39o/PjxmjRpkqVZFwp/9EFlqf85/o1m+/btvU+837p1q957771iT7y3wpkee311dt6X/d3x7DxmSv//ezz+WLZ//37vKFbneyyzez85ZuDAgQoLC9Njjz2mvLw89enTR9WrV1dqaqpjCxd/oXg5QXp6un7//XdJf57m+vnnn703o/nqZqrnn39evXr10tdff33SoWIfeOABy7L279+vXr16Wba+09m2bZt69OihH3/80TsKkfT/B3IrO+FRo0YpLS1Nq1evVpMmTdS+fXuNHDlSV111lWJiYizJmDVrlvf/hw8flsfjUfny5SVJO3bs0EcffaQmTZrouuuusyTveKtWrdL111+v0NBQ72n7KVOmaMKECZo/f75atWplWZadvzdJeuGFF2z9WUrSyy+/rClTpmjz5s2SpAYNGmjo0KEaOHCgpTll9e9N+vPM48cff6zWrVsrICBAtWrV0jXXXKPIyEilpKTohhtusDTvQmF3H1RW+5/3339f/fr1U9++fbV69Wrl5+dLkrKzszVhwgR9/vnn551xfJ/w1ltvqXfv3idd7uGHHz7vrOPZ0d+dyK5jpvTnsaxnz55at26dz49lduwnxxw+fFg9evRQ3759lZeXp/Xr12vZsmWqUaOGZRn+Yvt7Z0vvoHG4YzconurGRV/dwDhz5kwTFBRkwsPDTa1atUzt2rW9U506dSzNuvvuu82LL75o6TpPpWvXrqZbt25mz549Jjw83KSnp5uvv/7aXHrppWbJkiWWZrlcLlO5cmWTkpJiy43011xzjffnuH//flOlShVTo0YNExoaal544QXL86688krTv39/U1BQ4J1XUFBg7rzzTtOuXTtLs+z8vRlj/89y9OjRpnz58mbkyJHm448/Nh9//LEZOXKkCQ8Pt/wpwGX1782YP2/K3L59uzHmzyd+L1261BhjzLZt20xYWJjleRcCf/RBZbX/sfOJ98YYExUV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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "['man in a white shirt on a small boat in a lake.']"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sentence_list = [\n",
    "    \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "    # \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "    # \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "    # \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "]\n",
    "\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "translator = Translator(model.cpu(), tokenizer, tokenizer)\n",
    "translator(\n",
    "    sentence_list,\n",
    "    layer_idx=-1,\n",
    "    # heads_list=[0, 1, 2, 3, 4, 5, 6, 7]\n",
    "    )"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
